2010-03-30 29 views
4

此代碼有什麼問題? 在這裏,我們有兩個文件:classA.h和classB.hC++中的相互類實例

classA.h:

#ifndef _class_a_h_ 
#define _class_a_h_ 

#include "classB.h" 

class B; //???? 

class A 
{ 
public: 
    A() { 
     ptr_b = new B(); //???? 
    } 

    virtual ~A() { 
     if(ptr_b) delete ptr_b; //???? 
        num_a = 0; 
    } 

    int num_a; 
    B* ptr_b; //???? 
}; 

#endif //_class_a_h_ 

classB.h:

#ifndef _class_b_h_ 
#define _class_b_h_ 

#include "classA.h" 

class A; //???? 

class B 
{ 
public:  
    B() { 
     ptr_a = new A(); //???? 
        num_b = 0; 
    } 

    virtual ~B() { 
     if(ptr_a) delete ptr_a; //???? 
    } 

    int num_b; 
    A* ptr_a; //???? 
}; 

#endif //_class_b_h_ 

當我嘗試編譯,編譯器(g ++)說:

classB.h: In constructor ‘B::B()’:

classB.h:12: error: invalid use of incomplete type ‘struct A’

classB.h:6: error: forward declaration of ‘struct A’

classB.h: In destructor ‘virtual B::~B()’:

classB.h:16: warning: possible problem detected in invocation of delete operator:

classB.h:16: warning: invalid use of incomplete type ‘struct A’

classB.h:6: warning: forward declaration of ‘struct A’

classB.h:16: note: neither the destructor nor the class-specific operator delete will be

called, even if they are declared when the class is defined.

+2

每當類此耦合的,您的設計可能需要一些重構前擺好完成。 – GManNickG 2010-03-30 01:52:22

+0

@GMan:與實施迭代器模式的一個例外,非常同意。 +1。 – 2010-03-30 01:53:29

回答

9

不能創建一個不完整的類型的實例(編譯器不知道什麼關於班級!)

您需要將函數的定義(A和B的構造函數)移動到可包含頭文件的C++文件中(或者如果遵循約定,每個文件只有一個類,則可將其定義爲多個C++文件)。

之前已經說過,你編寫的代碼有一個嚴重的問題:每個A創建一個B的實例,每個B創建一個A的實例。最終會有一個無限遞歸,最終會導致內存不足。

兩個小問題:你不需要在調用delete之前測試一個指針是否爲null(可以安全地刪除一個空指針),並且你需要改變你的包含守護進程(名字以下劃線開始全局命名空間保留給實現)。

+1

+1遞歸 – Rado 2010-03-30 01:47:58

2

編輯:請先閱讀James McNellis的回答 - 這是您必須要做的一個代碼示例。但遞歸是更大的一點,他值得任何upvotes爲該特定點 - 不是我:)

您不能使用內聯函數作爲類A和B的完整定義不可用,當你宣佈他們內聯。將它們聲明爲普通函數,並且您可以使用前向聲明。

classA.h

#ifndef _class_a_h_ 
#define _class_a_h_ 

#include "classB.h" 

class B; //???? 

class A 
{ 
public: 
    A(); 
    virtual ~A(); 
    int num_a; 
    B* ptr_b; 
}; 

#endif //_class_a_h_ 

classB.h

#ifndef _class_b_h_ 
#define _class_b_h_ 

#include "classA.h" 

class B 
{ 
public:  
    B(); 
    virtual ~B(); 
    int num_b; 
    A* ptr_a; 
}; 

#endif //_class_b_h_ 

classes.cpp

#include "classA.h" 
#include "classB.h" 

A::A() { 
    ptr_b = new B(); //???? 
} 

A::~A() { 
    if(ptr_b) delete ptr_b; //???? 
} 

B::B() { 
    ptr_a = new A; //???? 
} 

B::~B() { 
    if(ptr_a) delete ptr_a; //???? 
} 
+0

但是...尋找無限遞歸! – 2010-03-30 01:49:15

+0

@Drew廳:好一點 - 沒想到這一點。但這裏不會竊取詹姆斯麥克奈利斯的觀點 - 他應該得到他們。 – 2010-03-30 01:49:55

+0

我認爲它根本不起作用。你有沒有試圖自己編譯它? – sepisoad 2010-03-30 02:22:28

1

classB.h: In constructor ‘B::B()’:

classB.h:12: error: invalid use of incomplete type ‘struct A’

A未完全定義。你只給它一個原型(class A;)。

classB.h:6: error: forward declaration of ‘struct A’

classB.h: In destructor ‘virtual B::~B()’:

認爲這是同樣的問題。它需要知道如何定義A,以便知道要釋放多少內存。

重構代碼以刪除循環依賴項。 (A創建B,B創建A ...創建B,創建A,創建B ...)

0

簡單的解決方案是將您的成員函數定義從線外拖入文件classA.cpp和classB.cpp。從頭文件中刪除相互包含,並將其插入到.cpp文件中。

Anywhere的A類或B用於不僅僅是在名稱更多(即,不僅僅是其命名指針或引用類型除外),一個完整的類聲明必須已經存在。使用目前的包含/內聯設計,其中一個或另一個不一定完整。通過拆分類實現了到.cpp文件,您允許的聲明類型爲A的實例化對象或B.