Hallo zusammen, ich habe zwei ganz einfache Headerdateien: -> one.h von class One und -> two.h von class Two Wenn ich schreibe: --------------------Datei one.h------------------------------ #pragma once #include "two.h" class One { public: Two * EinTwoObjekt; One(); ~One(); }; --------------------Datei tow.h------------------------------ #pragma once #include "one.h" class Two { public: One * EinOwoObjekt; Two(); ~Two(); }; Dann bekomme ich natürlich einen Fehler! Warum weil #include "one.h" und #include "two.h", selbst "one.h" oder "two.h" einbinden. Also so z.B. : --------------------Datei one.h------------------------------ #pragma once // #include "two.h" -> wird durch den Präprozessor ersetzt .... #include "one.h" -> und hier taucht jetzt "one.h" in der "one.h" auf .... -> und es kommt zu einem Fehler, oder ? class Two { public: One * EinOwoObjekt; Two(); ~Two(); }; class One { public: Two * EinTwoObjekt; One(); ~One(); }; --------------------Datei one.h END ------------------------------ Wenn ich aber schreibe: --------------------Datei one.h------------------------------ #pragma once #include "two.h" class Two; // Neu dazugekommen class One { public: Two * EinTwoObjekt; One(); ~One(); }; --------------------Datei tow.h------------------------------ #pragma once #include "one.h" class One; // Neu dazugekommen class Two { public: One * EinOwoObjekt; Two(); ~Two(); }; wird alles schön Kompiliert! -> Warum ist das so? -> Wie würdet Ihr das Problem lösen? DANKE.... IDE VS2010 Gruß c++
Forwärtsdeklarationen nutzen ist die Lösung. Zum Beispiel:
1 | #ifndef HEADER_ONE |
2 | #define HEADER_ONE |
3 | |
4 | class One; |
5 | |
6 | #include "two.h" |
7 | |
8 | class One { |
9 | private: |
10 | ... |
11 | }; |
12 | |
13 | #endif |
Du kannst am Ende die Includes sogar weglassen. Das Ganze nennt sich Forward Declaration. Das funktioniert, solange du nur mit Referenzen oder Pointern arbeitest. Man würde das genauso machen. Die Includes werden nur in den cpp-Dateien benötigt.
Header Dateien dürfen rekursiv eingebunden werden, jedoch sollte, wie bei allem das rekursiv arbeitet, eine Abbruchbedingung vorgesehen sein. Das Problem ist aber (ich nehme an, dass #pragma once deine Include Guards sind), dass du in beiden Klassen die jeweils andere verwenden willst. Denke wie ein Compiler: Du kannst Two nicht kompilieren, weil One noch nicht bekannt ist und du kannst One nicht kompilieren, weil Two noch nicht bekannt ist. c++ schrieb: > class One; // Neu dazugekommen Das ist eine forward declaration. c++ schrieb: > -> Wie würdet Ihr das Problem lösen? In diesem einfachen Fall mit einer forward declaration.
@ forward declaration -> danach habe ich gesucht.... Ich glaube so langsam kommt es an :-) DANKE
c++ schrieb: > -> Warum ist das so? Weil der Compiler wissen will, ob das was du benutzt auch tatsächlich existiert oder ob du vielleicht einen Tippfehler gemacht hast. > -> Wie würdet Ihr das Problem lösen? Das das ganze FOrward Deklaration heisst, wurde ja schon genannt. Mit einer Forward Deklaration
1 | class One; |
sagtst du dem Compiler also sinngemäss: es existiert eine Klasse namens 'One' in meinem Programm. Du verrätst hier keine Einzelheiten, also auch nicht wie diese Klasse aufgebaut ist, sondern du behauptest einfach nur: sie existiert. Damit ist der Compiler zufrieden und weiss daher, dass du hier
1 | class Two |
2 | {
|
3 | public:
|
4 | |
5 | One * EinOwoObjekt; |
keinen Tippfehler gemacht hast, sondern dass es tatsächlich etwas gibt, was 'One' heisst. Und da er weiss, wie gross ein Pointer ist, benötigt er auch keinerlei Informationen über die innere Struktur dieses 'One'. Etwas anderes wäre es, wenn du zum Beispiel über diesen Pointer eine Funktion des One-Objektes aufrufen willst
1 | class One; |
2 | |
3 | class Two |
4 | {
|
5 | public:
|
6 | |
7 | One * EinOwoObjekt; |
8 | |
9 | void doIt() { EinOwoObjekt->machWas(); } |
10 | ...
|
das klappt jetzt so auch nicht mehr. Denn selbstverständlich will der Compiler überprüfen, ob so ein One-Objekt auch wirklich über eine Member-Funktion namens 'machWas' verfügt. Das kann aber die Existenzbehauptung der Klasse One ein paar Zeilen darüber nicht leisten. Daraus ist ja nichts über den inneren Aufbau dieser Klasse ableitbar. Dazu muss der Compiler dann schon den kompletten Aufbau der Klasse vor der Verwendung im Funktionsaufruf gesehen haben.
:
Bearbeitet durch User
Karl H. schrieb: > class One; > > class Two > { > public: > > One * EinOwoObjekt; > > void doIt() { EinOwoObjekt->machWas(); } Jetzt ist bei mir endgültig der Groschen gefallen juhu :-) ---------------- one.h --------------------------------------- #pragma once #include "two.h" class Two; class One { public: Two * EinTwoObjekt; void doIt() { EinTwoObjekt->getI(); } One(); ~One(); }; Das geht :-)
c++ schrieb: > #include "one.h" -> und hier taucht jetzt "one.h" in der "one.h" auf > .... -> und es kommt zu einem Fehler, oder ? ..... und das "one.h" in "one.h" auftaucht stimmt ja sowas von nicht :-) jetzt ist es mir klar DANKE
c++ schrieb: > > ---------------- one.h --------------------------------------- > > #pragma once > > #include "two.h" > > class Two; > > class One > { > public: > > Two * EinTwoObjekt; > void doIt() { EinTwoObjekt->getI(); } > One(); > ~One(); > }; > > Das geht :-) An dieser Stelle ist dann die Forward Deklaration unsinnig. Denn entweder du inkludierst two.h welches die komplette Klassendefinition von Two einbringt, oder du machst eine Forward-Deklaration. Der Sinn einer Forward Deklaration besteht ja gerade darin, dass man (für eingeschränkte Zwecke) die vollständige Deklaration nicht benötigt. Hier benötigst du die vollstänsige Deklaration, denn anders wäre der Aufruf der Member Funktion nicht zu machen. Also muss der #include, über den sie bereit gestellt wird drinnen bleiben und die Forward-Deklaration ist an dieser Stelle unnötig (weil ja ohnehin schon vorher die vollständige Deklaration von Two eingebunden wurde). Denn das wäre ja ein bisschen sinnfrei, wenn du vorher den genauen Aufbau von irgendetwas herzeigen würdest nur um dann nachher nochmal (quasi "bekräftigend") auszudrücken "Jawohl, das existiert." Es ist in diesem Sinne nicht falsch, aber es ist sinnfrei.
:
Bearbeitet durch User
Karl H. schrieb: > Hier benötigst du die vollstänsige Deklaration, denn anders wäre der > Aufruf der Member Funktion nicht zu machen. Also muss der #include, über > den sie bereit gestellt wird drinnen bleiben und die Forward-Deklaration > ist an dieser Stelle unnötig (weil ja ohnehin schon vorher die > vollständige Deklaration von Two eingebunden wurde) Äh, stimmt :-)
Aber Probleme würde es geben wenn ich in beiden, also: in two.h int getI(void) { return i; } void doIt() { EinOwoObjekt->getI(); und in one.h int getI(void) { return i; } void doIt() { EinTwoObjekt->getI(); schreibe. Weil beide auf die vollständige komplette Klassendefinition zugreifen wollen , oder ?
c++ schrieb: > Weil beide auf die vollständige komplette Klassendefinition > zugreifen wollen , oder ? Ja. Du musst die Funktionen in eine separate Sourcedatei packen, in dem du die entsprechenden Header einbindest.
be s. schrieb: > Ja. Du musst die Funktionen in eine separate Sourcedatei packen, in dem > du die entsprechenden Header einbindest Alles klar vielen Dank -> gerade an Karl Heinz und be stucki und alle anderen natürlich. Jetzt mache Feierabend :-)
be s. schrieb: > c++ schrieb: >> Weil beide auf die vollständige komplette Klassendefinition >> zugreifen wollen , oder ? > > Ja. Du musst die Funktionen in eine separate Sourcedatei packen, in dem > du die entsprechenden Header einbindest. Es gibt eine Möglichkeit auch dieses Problem zu umgehen. Niemand sagt, dass die inline Funktionsrümpfe in der Klassendeklaration stehen müssen. Mit einer Mischung aus vorhergehender Forward-Deklaration und einem #include nach der Klassendeklaration lässt sich auch das in den Griff kriegen.
1 | #ifndef ONE_H
|
2 | #define ONE_H
|
3 | |
4 | class Two; |
5 | |
6 | class One |
7 | {
|
8 | ...
|
9 | |
10 | void doIt(); |
11 | void machWas(); |
12 | |
13 | Two* pTwo; |
14 | };
|
15 | |
16 | include "two.h" |
17 | |
18 | inline void One::DoIt() |
19 | {
|
20 | pTwo->machWas(); |
21 | }
|
22 | |
23 | #endif
|
1 | #ifndef TWO_H
|
2 | #define TWO_H
|
3 | |
4 | class One; |
5 | |
6 | class Two |
7 | {
|
8 | ...
|
9 | |
10 | void doIt(); |
11 | void machWas(); |
12 | |
13 | One* pOne; |
14 | };
|
15 | |
16 | include "one.h" |
17 | |
18 | inline void Two::DoIt() |
19 | {
|
20 | pOne->machWas(); |
21 | }
|
22 | |
23 | #endif
|
Aber erst mal soll er die ganz normale 'Straight Forward' Lösung in den Griff kriegen. Derartige enge Kopplungen zwischen Klassen sind eher selten und man sollte dann auch ins Auge fassen, ob man hier nicht überhaupt einen Designfehler hat.
Eine andere (jedoch zugegebenermassen nicht sehr elegante) Möglichkeit ist, beide Klassen als template du deklarieren. Die Implementierungen der Funktionen müssen dadurch erst bei der Instanziierung der template-Klassen bekannt sein.
1 | template<class S> class B_; |
2 | |
3 | template<class T> |
4 | struct A_ { |
5 | B_<T> b; |
6 | A_() { b.Foo(); } |
7 | };
|
8 | |
9 | template<class S> |
10 | struct B_ { |
11 | void Foo() {} |
12 | };
|
13 | |
14 | typedef A_<int> A; |
15 | typedef B_<int> B; |
16 | |
17 | void bar() { |
18 | A a; |
19 | B b; |
20 | }
|
Karl H. schrieb: > Derartige enge Kopplungen zwischen Klassen sind eher > selten und man sollte dann auch ins Auge fassen, ob man hier nicht > überhaupt einen Designfehler hat. Das stimmt natürlich :-)
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.