// main_pimpl_sample.cpp
#include "pimpl_sample.hpp"
using namespace std;
int main()
{
pimpl_sample p;
return 0;
}
// pimpl_sample.cpp
#include "pimpl_sample.hpp"
struct pimpl_sample::impl {
};
pimpl_sample::pimpl_sample()
: pimpl_(new impl) {
}
// pimpl_sample::~pimpl_sample()
// cause problem if missed
// {}
// pimpl_sample.hpp
#if !defined (PIMPL_SAMPLE)
#define PIMPL_SAMPLE
#include <boost/scoped_ptr.hpp>
class pimpl_sample {
struct impl;
boost::scoped_ptr<impl> pimpl_;
public:
pimpl_sample();
//~pimpl_sample(); cause problem if missed
void do_something();
};
#endif
~/Documents/C++/boost $ g++ --version
g++ (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2
~/Documents/C++/boost $ g++ -o main_pimpl_sample main_pimpl_sample.cpp pimpl_sample.cpp pimpl_sample.hpp
In file included from /usr/include/boost/smart_ptr/scoped_ptr.hpp:15:0,
from /usr/include/boost/scoped_ptr.hpp:14,
from pimpl_sample.hpp:6,
from main_pimpl_sample.cpp:2:
/usr/include/boost/checked_delete.hpp: In function ‘void boost::checked_delete(T*) [with T = pimpl_sample::impl]’:
/usr/include/boost/smart_ptr/scoped_ptr.hpp:80:9: instantiated from ‘boost::scoped_ptr<T>::~scoped_ptr() [with T = pimpl_sample::impl]’
pimpl_sample.hpp:8:20: instantiated from here
/usr/include/boost/checked_delete.hpp:32:58: error: invalid application of ‘sizeof’ to incomplete type ‘pimpl_sample::impl’
/usr/include/boost/checked_delete.hpp:32:58: error: creating array with negative size (‘-0x00000000000000001’)
Run Code Online (Sandbox Code Playgroud)
上面编译错误的解决方案是手动提供析构函数.所示原因如下:
你还必须记得手动定义析构函数; 原因是在编译器生成隐式析构函数时,类型impl是不完整的,因此不会调用其析构函数.
问题 >我仍然难以吸收上述想法,并想知道为什么我们必须在这里提供手动析构函数.
谢谢
TL; DR声明一个显式的析构函数并在代码模块中实现它(不在头文件中).
如果不创建析构函数,则编译器会在每个尝试销毁此类对象的转换单元中创建一个空的自动析构函数.如果在类头中定义了一个空的内联析构函数,则会得到相同的行为.
这会导致错误,因为析构函数还负责调用所有类字段的析构函数,这些字段按顺序需要实例化方法模板boost::scoped_ptr<impl>::~scoped_ptr();.反过来,此模板无法实例化,因为它尝试删除类型的对象impl,该对象仅在该范围内向前声明(并且您需要完整定义才能知道如何删除此对象.
OTOH,如果在头文件中声明非内联构造函数,则其代码仅在pimpl_sample.cpp其中生成,其中也包含定义impl,因此scoped_ptr的析构函数可以成功实例化.
其他翻译单元只调用pimpl_sample析构函数作为外部方法,因此他们不需要生成它并scoped_ptr自己实例化析构函数.