C++的前向声明
有些时候,尤其是写会提供给其他人使用的库的时候,不想暴露过多细节,也不想让使用库的人引入不必要的头文件,可以使用前向声明避免在头文件引入其他头文件而可以使用其中的类型。
说明
如前向声明的维基百科所述:
在程序设计中, 前向声明(Forward Declaration) 是指提前声明,但还没有给出完整的定义的标识符(表示编程的实体,如数据类型、变量、函数)。
简单来说,比如你想使用某个头文件的类classA
,但是为此引入一个头文件,甚至这个头文件会传递到用户侧,既可能导致编译时间不必要的增加,还违背了接口的目的暴露了细节。
那么可以使用如下写法:
|
|
如《C++ 类的前向声明的用法》所说:
C++的类可以进行前向声明。但是,仅仅进行前向声明而没有定义的类是不完整的,这样的类,只能用于定义指针、引用、以及用于函数形参的指针和引用。
而不能定义对象(因为此时编译器只知道这是个类,还不知道这个类的大小有多大),也不能访问类的对象,任何形式的访问都不允许(因为此时根本不知道有些什么成员)。等到类正式定义以后,就可以以各种方式使用该类了。
前向声明可以用于普通类、模板等情况,此外还可以结合智能指针,但是需要注意如下内容。
注意
当你将智能指针用于前向声明,可能会出现报错类型未定义,似乎是前向声明失效了。
不妨检查你的类是否没有构造和析构函数,或者在头文件中设置为了default
。
如《Is std::unique_ptr
It looks like current answers are not exactly nailing down why default constructor (or destructor) is problem but empty ones declared in cpp isn’t.
Here’s whats happening:
If outer class (i.e. MyClass) doesn’t have constructor or destructor then compiler generates the default ones. The problem with this is that compiler essentially inserts the default empty constructor/destructor in the .hpp file. This means that the code for default contructor/destructor gets compiled along with host executable’s binary, not along with your library’s binaries. However this definitions can’t really construct the partial classes. So when linker goes in your library’s binary and tries to get constructor/destructor, it doesn’t find any and you get error. If the constructor/destructor code was in your .cpp then your library binary has that available for linking.
This is nothing to do with using unique_ptr or shared_ptr and other answers seems to be possible confusing bug in old VC++ for unique_ptr implementation (VC++ 2015 works fine on my machine).
So moral of the story is that your header needs to remain free of any constructor/destructor definition. It can only contain their declaration. For example,
~MyClass()=default
; in hpp won’t work. If you allow compiler to insert default constructor or destructor, you will get a linker error.One other side note: If you are still getting this error even after you have constructor and destructor in cpp file then most likely the reason is that your library is not getting compiled properly. For example, one time I simply changed project type from Console to Library in VC++ and I got this error because VC++ did not added _LIB preprocessor symbol and that produced exact same error message.
看起来当前的答案并没有完全确定为什么默认构造函数(或析构函数)是有问题的,但在 cpp 中声明的空构造函数却不是。
这是发生的事情:
如果外部类(即 MyClass)没有构造函数或析构函数,则编译器会生成默认的构造函数或析构函数。问题是编译器本质上是在 .hpp 文件中插入默认的空构造函数/析构函数。这意味着默认构造函数/析构函数的代码与主机可执行文件的二进制文件一起编译,而不是与库的二进制文件一起编译。然而这个定义并不能真正构造分部类。因此,当链接器进入库的二进制文件并尝试获取构造函数/析构函数时,它找不到任何构造函数/析构函数,并且您会收到错误。如果构造函数/析构函数代码位于您的 .cpp 中,则您的库二进制文件可用于链接。
这与使用 unique_ptr 或共享_ptr 无关,其他答案似乎可能是旧 VC++ 中用于 unique_ptr 实现的令人困惑的错误(VC++ 2015 在我的机器上运行良好)。
所以这个故事的寓意是你的标题需要保持没有任何构造函数/析构函数定义。它只能包含他们的声明。例如,hpp 中的 ~MyClass()=default
; 不起作用。如果允许编译器插入默认构造函数或析构函数,您将收到链接器错误。
另一方面注意:如果即使在 cpp 文件中有构造函数和析构函数之后您仍然收到此错误,那么很可能的原因是您的库未正确编译。例如,有一次我只是在 VC++ 中将项目类型从控制台更改为库,然后出现此错误,因为 VC++ 没有添加 _LIB 预处理器符号,并且产生了完全相同的错误消息。
所以解决办法是在头文件只声明构造和析构,在源文件进行定义。例如Paul的回复:
-
my_class.h
1 2 3 4 5 6 7 8 9
#include <memory> class Thing; class MyClass { ~MyClass(); // <--- Added std::unique_ptr< Thing > my_thing; };
-
my_class.cpp
1
MyClass::~MyClass() = default; // Or a custom implementation