5
// 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’)

上述编译错误的解决方法是手动提供析构函数。指出的原因如下:

您仍然必须记住手动定义析构函数;原因是在编译器生成隐式析构函数时,类型 impl 不完整,因此不会调用其析构函数。

问题> 我仍然难以吸收上述想法,想知道一点细节,为什么我们必须在这里提供手动析构函数。

谢谢

4

1 回答 1

7

TL;DR声明一个显式析构函数并在代码模块中实现它(而不是在头文件中)。

如果您不创建析构函数,则编译器会在每个试图销毁此类对象的翻译单元中创建一个空的自动析构函数。如果您在类头中定义了一个空的内联析构函数,您将获得等效的行为。

这会导致错误,因为析构函数还负责调用所有类的字段的析构函数,而这些字段 - 按顺序 - 需要方法模板的实例化boost::scoped_ptr<impl>::~scoped_ptr();。反过来,该模板不能被实例化,因为它试图删除一个类型为 的对象impl,该对象仅在该范围内前向声明(并且您需要一个完整的定义才能知道如何删除该对象。


OTOH,如果你在header中声明了非内联构造函数,它的代码只生成在pimpl_sample.cpp,其中也有定义impl,所以scoped_ptr的析构函数可以成功实例化。

其他翻译单元则仅将pimpl_sample' 的析构函数作为外部方法调用,因此它们不需要自己生成并实例化scoped_ptr' 的析构函数。

于 2011-12-23T19:18:15.620 回答