考虑以下两种情况(编辑只是为了完成整个问题并使其更清晰)
情况 1:(无法编译,如下所述)
//B.h
#ifndef B_H
#define B_H
#include "B.h"
class A;
class B {
A obj;
public:
void printA_thruB();
};
#endif
//B.cpp
#include "B.h"
#include <iostream>
void B::printA_thruB(){
obj.printA();
}
//A.h;
#ifndef A_H
#define A_H
#include "A.h"
class A {
int a;
public:
A();
void printA();
};
#endif
//A.cpp
#include "A.h"
#include <iostream>
A::A(){
a=10;
}
void A::printA()
{
std::cout<<"A:"<<a<<std::endl;
}
//main.cpp
#include "B.h"
#include<iostream>
using namespace std;
int main()
{
B obj;
obj.printA_thruB();
}
案例2:(唯一的修改...可以正常工作,没有编译错误)
//B.h
#include "A.h" //Add this line
//class A; //comment out this line
我们假设 A.cpp 和 B.cpp 一起编译。上述两种情况有什么区别吗?有理由选择一种方法而不是另一种吗?
编辑: 那么我该如何让场景 1 发挥作用呢?
前向声明不能替代头文件包含。
顾名思义,前向声明只是一个
Declaration
而不是定义。
因此,您将声明编译器它是一个类,我只是在这里声明它,并在我要使用它时为您提供定义。因此,通常您在头文件中
forward declare
并在 .cpp 文件中 #include
,您将在其中使用前向声明类的成员。
通过这样做,你所做的是,无论你在哪里包含头文件,都只会有一个类的声明,而不是整个内容
#included
...
不过话说回来,当编译器要求类的定义时,应该是
#included
..
所以,在你的情况下
A obj;
需要class A
的定义,因此你应该#include
..
我自己问了一个类似的问题这里和另一个类似的问题,它也有一个很好的答案......
希望有帮助..
情况1在编译B.cpp时会产生“类型不完整”错误。因为 B 类包含 A 类对象,所以 A 类的定义(特别是大小)需要在 B 类的定义之前完成。
或者,您可以选择将 some_variable 设置为 A 类的指针或引用,在这种情况下,您的前向声明在 B.h 中就足够了。您仍然需要 B.cpp 中 A 的完整定义(假设您实际使用了 A 成员函数/数据)。
如果您有相互引用的类,则需要使用前向声明。
//A.h
class B;
class A {
B* someVar;
}
//B.h
#include <A.h>
class B {
A* someVar;
}
但是在你列出的情况下这样做没有任何好处。
像编译器一样思考。为了在
A
内部创建 B
,编译器必须知道如何构建 A
,而唯一的方法就是拥有完整的定义。前向声明告诉编译器类A
存在,但没有描述它的样子;这足以定义指针或引用。当需要使用该指针或引用时,将需要完整的类定义。
如果您打算将 some_variable 描绘为指针,那么经常推荐的做法是尽可能使用前向声明,以避免包含的开销和更长的编译时间。
我完全支持最佳实践,但我真的很喜欢使用具有良好代码导航功能的 IDE,并且转发会导致问题,至少对于 Netbeans 来说是这样。每当我尝试导航到类型声明时,我总是会到达前面,而不是包含实际声明的 .h 文件。为了便于导航,我愿意接受一些额外的编译时间。也许这只是 Netbeans 的问题:)
..哦是的..如果您查看问题右侧的相关问题,您会发现很多前向声明的附加信息。
对于情况1,编译器会抱怨B类“类型不完整”,因为B类包含一个A类对象,而你没有告诉B任何A类的细节,所以编译器无法决定B类对象的大小。
对于您的情况,您可以使用
A& obj
或 A* obj
代替 A obj
,因为引用/指针的大小是 const(32 位/64 位 CPU 为 4/8)。