PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Zyklische Abhängigkeiten


Gast
2006-10-04, 21:55:02
Ich habe ein Problem bei C++ mit einer zyklischen Abhängigkeit, die sich auch nicht per forward declarartion lösen lässt.
Es gibt zwei Klassen in drei Dateien:

test2.h:

#ifndef TEST2_H
#define TEST2_H

#include "test.h"

class test;

class test2
{
public:
test t;
};
#endif

test.h:

#ifndef TEST_H
#define TEST_H

#include "test2.h"
class test2;
extern test2 t2;

class test
{
void doSomething()
{
t2.t.bla = 0;
}

private:
int bla;
};
#endif

test.cpp:

#include "test.h"
#include "test2.h"

test2 t2;

int main()
{
return 0;
}
Der Compiler meckert:In file included from test.h:4,
from test.cpp:1:
test2.h:11: error: field `t' has incomplete type

Wie kann ich das Lösen? Die beiden Klassen möchte ich nicht in eine Datei packen, weil diese in Echt sehr viel größer sind (ich hab's hier auf's Wesentliche reduziert) und es daher unübersichtlich wird.

Expandable
2006-10-04, 23:24:34
Brauchst Du das #include "test.h" in der test2.h wirklich? Weil eigentlich ist ja der Sinn der forward declaration, dass die entsprechende Header-Datei eben NICHT mehr inkludiert werden muss.

muhkuh_rs
2006-10-04, 23:28:54
Forward declaration erlaubt es, Pointer oder Referenzen auf einen Typ anzulegen, obwohl dieser noch nicht genau definiert ist. Du versuchst aber, eine Membervariable in einer Klasse mit einem nicht genau definierten Typ anzulegen:


class test;

class test2
{
public:
test t;
};


Der Compiler muss an dieser Stelle wissen, wieviel Platz eine Instanz von test einnimmt. Da er nur eine Forward-declaration hat, kann er das nicht. Was Du allerdings machen kannst, ist eine Referenz oder einen Pointer auf eine test Instanz anlegen. Die Größe von Pointern und Referenzen kennt der Compiler.


//test2.h
class test;

class test2
{
public:
test2();
~test2();
test * t;
};


Im cpp file kann man dann die eigentliche Instanz von test erzeugen und den Pointer belegen:


// test2.cpp
#include "test2.h"
#include "test.h"

test2::test2()
{
t=new test();
}

test2::~test2()
{
delete t;
}


So weit so gut. Dein zweiter Fehler ist, dass Du trotz forward declaration von
test vorher noch test.h inkludierst. Es ergibt keinen Sinn, erst test genau zu definieren und dann noch eine forward declaration zu machen. Außerdem führt das zum zyklischen Inkludieren. Komplett sollte test2.h also so aussehen:

#ifndef TEST2_H
#define TEST2_H

class test;

class test2
{
public:
test2();
~test2();
test * t;
};
#endif


und test.h so:

#ifndef TEST_H
#define TEST_H

#include "test2.h"
//class test2;
extern test2 t2;

class test
{
void doSomething()
{
t2.t.bla = 0;
}

private:
int bla;
};
#endif

Xmas
2006-10-04, 23:31:12
Du kannst die Implementierung der Methode test::doSomething hinter die Klassendeklaration schieben. Wenn es nicht gerade für Inlining wichtig ist sollten in der Header-Datei sowieso nur Deklarationen und keine Definitionen stehen. (Aber nicht so wichtig). Warum ist doSomething nicht static?

muhkuh_rs,
Statt t2.t.bla muss es dann aber t2.t->bla heißen da t ja nun ein Pointer ist.

Gast
2006-10-04, 23:52:21
Danke für die Antworten. Die Möglichkeit mit dem Pointer ist mir auch schon gekommen, wäre aber estetisch nicht optimal. Aber angesichts der Möglichkeiten, die funktionieren, muss ich mich wohl damit begnügen.
Du kannst die Implementierung der Methode test::doSomething hinter die Klassendeklaration schieben. Wenn es nicht gerade für Inlining wichtig ist sollten in der Header-Datei sowieso nur Deklarationen und keine Definitionen stehen. (Aber nicht so wichtig).Ändert das was an dem Problem der Abhängigkeiten? Ich denke nicht. Aber Recht hast du natürlich.
Warum ist doSomething nicht static?Es werden noch ein paar andere Dinge dort gemacht, die hier nicht aufgeführt sind (u.a. auf Member von test zugreifen, die hier auch nicht genannt sind).

muhkuh_rs
2006-10-05, 09:48:29
Wenn zwei Klassen gegenseitig voneinander abhängen, gibt es meistens 2 Möglichkeiten das aufzulösen. Wenn das mit dem Pointer und dem damit verbundenen Aufwand problematisch ist, dann eben umgekehrt:

test2.h:

#ifndef TEST2_H
#define TEST2_H

#include "test.h"

class test2
{
public:
test t;
};
#endif


test.h:

#ifndef TEST_H
#define TEST_H

//#include "test2.h"
class test2;
//extern test2 t2;

class test
{
void doSomething();
// {
// t2.t.bla = 0;
// }

private:
int bla;
};
#endif


test.cpp:

#include "test2.h"
extern test2 t2;
void test::doSomething()
{
t2.t.bla = 0;
}