C++ 前置定义 Forward declaration
在编写相对大型的CPP工程的时候,往往我们会遇到一个情况,即:
// A.hpp
#include "B.hpp"
class A{
};
// B.hpp
#include "A.hpp"
class B{
};
这往往会触发编译器的报错。为了解决这种循环引用头文件的问题,我们可以使用c++的前置引用(forward declaration)。
// A.hpp
class B; // ** forward declaration **
class A{
};
// B.hpp
#include "A.hpp"
class B{
};
我们选择在相对比较基础的头文件中使用前置定义,定义出 class B
, 这可以简单理解为, 当编译器在处理A.hpp
的时候我们告诉了编译器,这个B
虽然没有出现在当前文件的include
里面,但是它之后会被创建。
Note
使用前置定义的时候也有需要注意的地方,分为以下几点。
假设我们使用前置定义声明了一个class
, 通常我们称这个类为incomplete type
class X;
可以的操作有:
What you can do with an incomplete type:
-
作为成员类型,注意 此处只能声明指针或者引用:
class Foo { X *p; X &r; };
-
定义函数,作为形参或者返回类型, 注意这里的函数定义,与下面的函数声明的区别,只有类型:
void f1(X); X f2();
-
使用该类型的指针或者引用:
void f3(X*, X&) {} X& f4() {} X* f5() {}
What you cannot do with an incomplete type:
-
作为基类
class Foo : X {} // compiler error!
-
作为成员类型,声明成员变量(因为我们不知道该类型的具体大小,内存分布):
class Foo { X m; // compiler error! };
-
定义该类型的返回值或者形参, 这里与上面的不同在于实现了 函数体。这往往会涉及到该类型的 拷贝构造函数 或者 赋值构造函数。
void f1(X x) {} // compiler error! X f2() {} // compiler error!
-
使用该类型的方法,或者成员变量。
class Foo { X *m; void method() { m->someMethod(); // compiler error! int i = m->someField; // compiler error! } };