C++ #include Loop
C++ #include Loop
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
成员变量…
如果不使用指针破坏它,就会导致无限递归成员变量占用无限内存,这是不可能的。
注意,由于无限递归,编译器甚至不能实际计算Dog
或Cat
所需的内存。但是由于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++中,class
和struct
关键字之间并没有什么实际的区别,除了一个类默认是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
知道Dog
和Dog
知道Cat
,使用指针代替对象。这是因为指针总是有相同的内存大小,所以预编译器不需要扫描Cat.h
来"学习"。必须为它分配多少内存。换句话说,它已经知道它需要多少内存,所以它避免了这个问题。
提示:如果你有几十个或几百个类,它们应该都知道彼此,你就会陷入厄运的循环层次结构。有一种方法可以避免这一切,并为您在大型c++项目中省去麻烦:
创建一个PreDefine.h
头文件,其中根据一些预编译器规则预定义所有类(并且只使用指针)。然后,您可以在任何需要的地方包含PreDefine.h
。这样,你可以创建数百个相互链接的类,但仍然不会遇到include-loop错误。
在这个例子中,我们有一个Cat
, Dog
和Mouse
类,但你可以有尽可能多的类。
#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"
- 既然存在危险,为什么项目要使用-I include开关
- 有充分的理由在h文件中使用include保护而不是cpp文件吗
- 为什么我不能在 FOR LOOP 中使用 i/10,C++?
- Arduino:for/while/if在void setup()或void loop()之前?——错误:之前需要不合格
- 如何将更多文件夹添加到c++include路径
- 什么是"#include <boost/functional/hash.hpp> "?
- 对于MacOS上的G++,如何添加默认的include目录/usr/local/include和默认的库搜索路径/usr
- C++包含来自 #include "DevEngine/Core.h" 的错误
- <filesystem> 在 clang 6 和 10 上 #include 错误
- 在 void 函数中使用 #include 变量C++
- "Ill-defined for-loop - loop executes infinitely" (MSVC C6295)
- N-API include an .so or dll
- Arduino:在 loop() 和自定义函数中运行相同的代码时出现问题
- 允许哪些令牌作为 #include 的参数?
- GCC 包含标头(使用"-include")CMake 未检测到的更改
- WebAssembly include OpenCV
- 如何在 c++ 中正确指定 #include 路径以使程序可移植
- 修改生成文件以简化框架 #include 路径
- VS2015/VC++ 在新类模板中禁用默认 #include "stdafx.h"
- C++ #include Loop