2016-06-17 11 views
0

Ich habe Probleme mit der zirkulären Abhängigkeit zwischen den Klassen A, B, C. Der Benutzertyp cfunction aus Klasse A verweist auf die statische Methode von C :: F1. Hier ist der Code:C++, zirkuläre Abhängigkeiten, Vorlagen und Benutzertypen

Datei A. h

#ifndef A_H 
#define A_H 

#include "C.h" 
class C; 

template <typename T> using cfunction = T(*)(T, T); 

template <typename T> 
class A{ 
    public: 
     cfunction <T> X; 
     A() : X(&C::F1) {} 
     A (const cfunction <T> &pX) : X(pX){}; 
     virtual ~A() = 0; 
}; 
#endif 

Datei B.h

#ifndef B_H 
#define B_H 
#include "A.h" 

template <typename T> 
class B : public A <T> { 
    public: 
    B() : A<T>(), b(0.0) {} 
    B(const T b_, const cfunction <T> &pX) : A <T>(pX), b(b_){} 
    virtual ~B() {}; 
}; 
#endif 

Schließlich wird bei dem Verfahren init() von C ein gemeinsamer Zeiger auf A gespeichert wird. Die Methode F1 ruft F2 mit dem Template-Parameter F3 auf. Hier ist der Code:

Datei C. H

#ifndef C_H 
#define C_H 

#include "A.h" 
template <typename T> 
class A; 

#include <memory> 
#include <list> 

template <typename T> 
using List = std::list<std::shared_ptr<A<T> > >; 

//Definition of all projections 
class C { 
    public: 
     template <typename T, typename Function> static T F2(Function f, const T a, const T b); 
     template <typename T> static void init(List<T> l); 
     template <typename T> static T F1(const T a, const T b); 
     template <typename T> static T F3(const T a, const T b); 
}; 
#include "C.hpp" 
#endif 

Datei C.hpp

#ifndef C_HPP 
#define C_HPP 

#include "B.h" 
template <typename T> 
class B; 

template <typename T, typename Function> 
T C::F2(Function f, const T a, const T b) { return f(a, b);} 

template <typename T> void C::init(List<T> l) { 
    auto test = std::make_shared <B <T>> (0.0, F1<T>); 
    l.push_back(test); 
} 

template <typename T> T C::F1(const T a, const T b) { return F2(F3<T>, a, b);} 
template <typename T> T C::F3(const T a, const T b) {return a + b;} 

#endif 

Die Hauptdatei: main.cpp

#include "C.h" 

int main(){ 
    List <double> l; 
    C::init(l); 
    return 0; 
} 

Sorry für die etwas komplizierten Code. Eine einfachere Version des Codes funktioniert gut, aber diese "volle" Variante trifft zu. Ich bin nicht in der Lage, das Problem für g ++ zu beheben; Übersetzungsoptionen: -std = C++ 11.

Danke für Ihre Hilfe ...

+1

nicht beide '# include' und forward-declare (vorwärts-deklarieren "nach der Tat" ist sinnlos). Da Sie die Definition von "A" in "C.h" nicht benötigen, verschieben Sie ihr "# include" auf "C.hpp". Da Sie die Definition von "B" in "C.hpp" benötigen, entfernen Sie ihre Deklaration. – molbdnilo

Antwort

1

OK, so kann Ihr Problem mit ein paar kleineren Anpassungen gelöst werden. Wie Sie bemerken, haben Sie zur Zeit einige zirkuläre Abhängigkeiten, aber sie können mit nur einer fundamentalen und etwas leichten Modifikation gebrochen werden: Entfernen Sie nämlich den A Standardkonstruktor, der auf C verweist. Sie brauchen es nicht wirklich - so wie es aussieht, benutzt Ihr Code es nicht. Selbst wenn Sie das getan haben, können Sie einfach X member auf nullptr setzen und später extern initialisieren.

Damit entfernt man nun eine einfache Integration Auftrag haben kann: A.h, B.h, C.h, C.hpp.

Es gibt ein paar andere Compilerfehler, die ich danach beheben musste: Sie scheinen ein nicht existierendes B Mitglied b für eins zu initiieren. Auch wenn Ihr Destruktor A rein virtuell ist, benötigt er eine Definition.Endgültiger Code unten:

BEARBEITEN (2): Ich habe das jetzt so geändert, dass der Standardkonstruktor für A nicht länger ausgeschlossen ist. Es wird stattdessen einfach später definiert, in C.h, nachdem die Definition C verfügbar ist.

A. h:

#ifndef A_H 
#define A_H 

//#include "C.h" 
//class C; 

template <typename T> using cfunction = T(*)(T, T); 

template <typename T> 
class A{ 
    public: 
     cfunction <T> X; 
     //A() : X(&C::F1) {} 
     A(); 
     A (const cfunction <T> &pX) : X(pX){}; 
     virtual ~A() = 0; 
}; 

template <typename T> 
A<T>::~A() {} 

#endif 

B.h:

#ifndef B_H 
#define B_H 
#include "A.h" 

template <typename T> 
class B : public A <T> { 
    public: 
    B() : A<T>() //, b(0.0) 
    {} 
    B(const T b_, const cfunction <T> &pX) : A <T>(pX) //, b(b_) 
    {} 
    virtual ~B() {}; 
}; 
#endif 

C. H:

#ifndef C_H 
#define C_H 

#include "A.h" 
#include "B.h" 

//template <typename T> 
//class A; 

#include <memory> 
#include <list> 

template <typename T> 
using List = std::list<std::shared_ptr<A<T> > >; 

//Definition of all projections 
class C { 
    public: 
     template <typename T, typename Function> static T F2(Function f, const T a, const T b); 
     template <typename T> static void init(List<T> l); 
     template <typename T> static T F1(const T a, const T b); 
     template <typename T> static T F3(const T a, const T b); 
}; 

template<typename T> 
A<T>::A() : X(&C::F1) 
{} 

#include "C.hpp" 
#endif 

C.hpp:

#ifndef C_HPP 
#define C_HPP 

//#include "B.h" 
//template <typename T> 
//class B; 

template <typename T, typename Function> 
T C::F2(Function f, const T a, const T b) { return f(a, b);} 

template <typename T> void C::init(List<T> l) { 
    auto test = std::make_shared <B <T>> (0.0, F1<T>); 
    l.push_back(test); 
} 

template <typename T> T C::F1(const T a, const T b) { return F2(F3<T>, a, b);} 
template <typename T> T C::F3(const T a, const T b) {return a + b;} 

#endif 
+0

@Smeethey: danke, aber was zu tun ist, wenn X (& C :: F1) {} erforderlich ist? Dies ist ein vereinfachtes Beispiel und die oben erwähnte Initialisierung ist wichtig ... – justik

+0

Erkläre, warum es notwendig ist? Meinst du nur, du brauchst einen Standardkonstruktor für 'A'? Wenn ja, empfehle ich,' X' auf 'nullptr' im besagten Konstruktor zu setzen und dann eine Funktion wie' void setX (cfunction val) hinzuzufügen {X = val;} ', dann von außen aufrufen. Kannst du sonst noch klären, warum du es brauchst? – Smeeheey

+0

Es ist ein relativ komplexes Modell, bei dem B keine einzelne Klasse ist, sondern mehr als 20 Klassen abgeleitet sind von A (B ist ein Beispiel). Es ist unbequem Die Initialisierung kann "zweimal" durchgeführt werden. In Bezug auf meine Frage interessiert mich auch, wie dieses Problem vollständig gelöst werden kann (nicht nur eine vereinfachte Version). Ich habe den größten Teil des Morgens damit verbracht, das Problem zu lösen :-) – justik

0

B.h A. h beinhalten muss (da braucht es seine Besonderheiten kennen)

A. h C. H umfassen muss (weil es seine Besonderheiten kennen muss). Dadurch, dass es muss nicht C wegen zu zukunfts erklärt es schon bekannt durch die einschließlich von Ch

Aber aus dem Code, Ch nicht braucht über die Besonderheiten von A zu kennen und also brauche ich nicht Ah Stattdessen kann es A einfach weiterleiten, wie es bereits geschieht

Auf eine ähnliche Anmerkung, C.hpp muss B nicht enthalten, da es auch keine der Spezifika von B wissen muss. Eine einfache Vorwärts-Deklaration (wie zum Beispiel, was Sie bereits tun) sollte ausreichen

Immer wenn Sie sehen, dass Sie eine Datei enthalten und eine Klasse weiterleiten, die Sie aus dieser Datei zur gleichen Zeit erhalten haben, bedeutet das, dass Sie etwas davon haben tun

+0

@ Altainia: Leider funktioniert es nicht für mich: Ah (#include "Ch"), Bh (#include "Ah), Ch (Vorlage Klasse A;), und C.hpp (Vorlage Klasse B;) Es ist nicht so einfach wie es aussieht .. – justik