C++ #include Loop

C++ #include Loop

本文关键字:Loop #include C++      更新时间:2023-10-16

File Dog.h

#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

这会导致循环,问题是两个类都需要知道彼此;前向声明也不能解决问题。您可以创建狗d或猫c的指针来解决它。

问题:是否真的没有办法不创建指针,即保持代码原样,而不重写我想要的方式?

你想做的事是不可能的。你的类会占用无限的空间。

你有一个Dog,它有一个Cat成员变量,它有一个Dog成员变量,它有一个Cat成员变量,它有一个Dog成员变量…

如果不使用指针破坏它,就会导致无限递归成员变量占用无限内存,这是不可能的。

注意,由于无限递归,编译器甚至不能实际计算DogCat所需的内存。但是由于c++中的类通常不能达到0大小,我们知道它无论如何都将是无限空间。

其他答案也给出了很好的视角,即。"鸡和蛋"的问题,必须先定义其中一个才能定义另一个。


我只是想补充一下,相互递归的类是很奇怪的,如果你能以其他方式设计它,最好避免。如果你真的必须使用它们,确保你真的打破了递归性。否则,如果你有一个简单的实现,在构造时使用指针进行无限的相互递归new调用,那么你只能将它从编译时由于无限相互递归而导致的错误转移到内存不足和运行时崩溃。

不,这是不可能的,它与包含文件无关。

原因是,如果创建的类包含另一个类作为成员(而不是指针),编译器需要在另一个类中为该类添加一些空间。例如,如果您创建这样的内容:

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

那么在上面的例子中,Person类的实例的size为sizeof(std::string) + sizeof(Dog),而Dog类的实例的size为sizeof(std::string) * 2;所以Person的size是sizeof(std::string) * 3(模对差)

换句话说,c++中一级变量(与指针相反)所需的内存是作为实例的一部分分配给的,而不是指针;这与struct的工作方式非常相似。事实上,在c++中,classstruct关键字之间并没有什么实际的区别,除了一个类默认是private,而一个结构是public

除了导致无限空间对象(如Raphael所解释的)之外,编译器不会像这个问题中解释的那样让这种情况发生。当你在Dog类中使用它时,他不知道Cat类,所以它不会因为同样的原因而不起作用:

class B;
class A{
    B b;
};

前面已经指出,如果没有间接,就不能直接或间接地定义一个将自身作为成员的对象。

如果你只想使用.语法而不是->语法,你可以使用引用而不是指针。例如:

// 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) {}

用指针代替。

问题是,为了让预编译器知道Dog的内存大小,它还需要知道为Cat分配多少内存。然而,Cat需要知道Dog使用了多少内存,你会得到一个无限循环。

如果你想让Cat知道DogDog知道Cat,使用指针代替对象。这是因为指针总是有相同的内存大小,所以预编译器不需要扫描Cat.h来"学习"。必须为它分配多少内存。换句话说,它已经知道它需要多少内存,所以它避免了这个问题。

提示:如果你有几十个或几百个类,它们应该都知道彼此,你就会陷入厄运的循环层次结构。有一种方法可以避免这一切,并为您在大型c++项目中省去麻烦:

创建一个PreDefine.h头文件,其中根据一些预编译器规则预定义所有类(并且只使用指针)。然后,您可以在任何需要的地方包含PreDefine.h。这样,你可以创建数百个相互链接的类,但仍然不会遇到include-loop错误。

在这个例子中,我们有一个Cat, DogMouse类,但你可以有尽可能多的类。

在PreDefine.h:

#pragma once
#if !defined Dog_H && !defined Dog_TEMP
#define Dog_TEMP
class Dog;
#endif
#if !defined Cat_H && !defined Cat_TEMP
#define Cat_TEMP
class Cat;
#endif
#if !defined Mouse_H && !defined Mouse_TEMP
#define Mouse_TEMP
class Mouse;
#endif
//...
//this pattern for each class you have
//...
在Dog.h:

#pragma once
#include "PreDefine.h" // <-- include this in all header files, and all your problems goes away.
class Dog
{
    Cat * c;  // <- must be a pointer
    Mouse * m; // <- must be a pointer
};
#define Dog_H // <- need this at end of class to stop any future overwrites of class.
在Dog.cpp:

#include "Cat.h" //<-- Include real header in .cpp files where you need it
#include "Mouse.h"//<-- Include real header in .cpp files where you need it
... your code here...

在Cat.h、Cat.cpp、Mouse.h和Mouse.cpp.

中使用相同的模式在Cat.h:

#pragma once
#include "PreDefine.h"
class Cat
{
    Dog * d;  
    Mouse * m; 
};
#define Cat_H 
在Mouse.h:

#pragma once
#include "PreDefine.h"
class Mouse
{
    Cat * c;  
    Dog * d; 
};
#define Mouse_H 
在Cat.cpp:

#include "Dog.h" 
#include "Mouse.h"
在Mouse.cpp:

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