2015-05-27 32 views
2

文件Dog.hC++的#include環

#ifndef DOG_H 
#define DOG_H 

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

class Dog 
{ 
     Cat c; 
}; 

#endif 

文件Cat.h

#ifndef CAT_H 
#define CAT_H 

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

class Cat 
{ 
     Dog d; 
}; 

#endif 

這將導致一個循環,問題是,這兩個類需要了解對方;前向聲明也不能解決問題。您可以創建Dog d或Cat c的指針來解決它。

問題:真的沒有辦法不創建指針,即保持代碼原樣而不重寫我希望它是什麼?

+1

爲什麼前聲明不解決呢? – dlavila

+0

答案:不會。你會如何構建一個包含它自己的對象?如果你認爲你需要這樣的事情,你也應該重新考慮你的設計。 – StoryTeller

+0

這不可能是你想要的,因爲即使你可以解決聲明的順序,在每個類中包含另一個類在邏輯上是不可能的。必須有一個指針或參考;而且不需要包含標題。 –

回答

5

你想要做的事情是不可能的。你的班級將佔用無限的空間。

你有Dog具有Cat成員變量,其中有一個Dog成員變量,其中有一個Cat成員變量,其中有一個Dog成員變量...

沒有用指針的地方打破它你會以無限的遞歸成員變量結束無限的記憶,這是不可能的。

請注意,由於無限遞歸,編譯器甚至無法實際計算DogCat所需的內存。但是由於C++中的類通常不能是0大小,我們知道它無論如何都是無限的空間。

其他答案也給出了很好的觀點,即'雞和蛋'問題,其中一個必須首先定義爲能夠定義另一個。


我只想補充一點,相互遞歸類是一些奇怪的事情,如果你可以把它設計一些其他的方式,最好避免。如果你真的必須使用它們,確保你實際上破壞了遞歸。否則,在編譯時由於無限的相互遞歸導致內存不足以及運行時崩潰,如果在構造時使用指針進行無限遞歸調用的天真實現,則只會在編譯時將其從錯誤中移出。

+0

這是真的。但我認爲這不是錯誤的原因,請看我的回答(如果我錯了,請糾正我) –

1

不,這是不可能的,它與包含文件無關。

原因是,如果您創建一個包含另一個類作爲成員(而不是指針)的類,編譯器需要爲該類添加一些空間到另一個類中。例如,如果你創造的東西是這樣的:類Person在上面的例子中

class Dog { 
    std::string name; 
    std::string owner; 
}; 

class Person { 
    Dog pet; 
    std::string name; 
}; 

然後實例有一個大小爲sizeof(std::string) + sizeof(Dog),而類Dog的情況下,有一個大小是sizeof(std::string) * 2;所以人是在大小sizeof(std::string) * 3(模對準差異)

換句話說,在C++中用於第一級的變量所需(而不是一個指針)的存儲器被分配實例的一部分,並且是不指針;這和struct的做法非常相似。實際上,C++中的classstruct關鍵字之間確實沒有實際的區別,除了缺省類爲private,而結構爲public

1

除了這導致一個無限的空間物體(如Raphael解釋),編譯器不會讓這種情況發生,如question所述。他不知道Cat類當您在Dog類使用它,所以它不會爲同樣的原因,這是行不通的工作:

class B; 
class A{ 
    B b; 
}; 
+1

看到它的另一種方法。我想這可能會擴展到「雞和雞蛋」問題,因爲在定義另一個類之前必須先定義一個類,但是不能說哪一個類。 –

+0

@RaphaelMiedl正確 –

1

前面已經指出的那樣,你不能直接或間接定義一個自身作爲成員而不間接的對象。

如果您只希望能夠使用.語法而不是->語法,則可以使用引用而不是指針。例如:

// in Dog.h 
class Cat; 

class Dog { 
public: 
    std::unique_ptr<Cat> catp; 
    Cat &c; 
    Dog(); 
    Dog (Cat &); 
}; 

// in Cat.h 
class Cat { 
public: 
    std::unique_ptr<Dog> dogp; 
    Dog &d; 
    Cat(); 
    Cat (Dog &); 
} 

現在,在源代碼中,你可以做這樣的事情:

#include "Dog.h" 
#include "Cat.h" 

Dog::Dog() : catp(new Cat(*this)), c(*catp) {} 
Cat::Cat() : dogp(new Dog(*this)), d(*dogp) {} 

Dog::Dog (Cat &cat) : c(cat) {} 
Cat::Cat (Dog &dog) : d(dog) {} 
+0

我只是想添加一個警告,這應該只是作爲一個例子。一個天真的實現只會在編譯時將問題轉移到無限遞歸的「新」調用中。注意你真的有辦法打破遞歸。 –