【问题标题】:Pimpl with smart ptr - Why constructor/destructor needed [duplicate]Pimpl with smart ptr - 为什么需要构造函数/析构函数[重复]
【发布时间】:2014-03-09 01:58:53
【问题描述】:

让我们考虑以下示例(使用 c++11)

A.hpp:

#include <memory>
class A
{
  public:
    //A();
    //~A();

  private:
    struct AImpl;
    std::unique_ptr<AImpl> pImpl;
};

main.cpp:

#include "A.hpp"

int main()
{
    A a;
}

使用默认构造函数和析构函数。不编译。出现以下错误:

在 /usr/include/c++/4.8/memory:81:0 包含的文件中, 来自 A.hpp:2, 来自 main.cpp:2: /usr/include/c++/4.8/bits/unique_ptr.h: 在 'void std::default_delete<_tp>::operator()(_Tp*) const [with _Tp = A::AImpl]': /usr/include/c++/4.8/bits/unique_ptr.h:184:16: 必需 来自 'std::unique_ptr<_tp _dp>::~unique_ptr() [with _Tp = A::AImpl; _Dp = std::default_delete]' A.hpp:3:7: 这里需要 /usr/include/c++/4.8/bits/unique_ptr.h:65:22: 错误: 无效 将“sizeof”应用于不完整类型“A::AImpl”
static_assert(sizeof(_Tp)>0,

使用 boost::scoped_ptr 而不是 std::unique_ptr 时会发生类似的错误。我是否理解正确 - 这意味着 AImpl 的前向声明还不够?

添加构造函数和析构函数时,一切正常。是什么原因?是因为默认值是内联的,因此看不到 AImpl 的大小吗?而在添加构造函数和析构函数时,编译器会假设这些定义知道 AImpl 的大小?

【问题讨论】:

  • this GOTW 对此问题进行了很好的讨论。

标签: c++ c++11 pimpl-idiom


【解决方案1】:

unique_ptr 析构函数需要知道AImpl 的完整定义,因为它会删除它。所以问题是,unique_ptr 析构函数在哪里?它是一个模板,所以问题是关于实例化点。

析构函数在第一次使用时被实例化。包含类的构造函数和析构函数都使用它(如果构造函数的主体抛出异常,则构造函数需要它)。所以unique_ptr 析构函数在A 的构造函数或析构函数放置的地方实例化,以先到者为准。

如果您默认这些特殊成员,它们会在类主体之后立即生成,即在标题中,AImpl 的大小未知。

如果您改为在类中声明它们,然后将定义(您可以 =default 那些定义)放在 .cpp 中,在 AImpl 的完整定义之后,那么 unique_ptr 析构函数会在那里实例化。

【讨论】:

  • 析构函数不需要知道大小;内存管理会解决这个问题。但是,它确实需要知道如何破坏对象,并且假设有一个完整的类(要知道是否存在重要的析构函数)。
  • @JamesKanze 好点,已修复。
猜你喜欢
  • 1970-01-01
  • 2011-04-17
  • 2012-09-15
  • 1970-01-01
  • 2020-01-06
  • 2020-03-10
  • 1970-01-01
  • 1970-01-01
  • 2017-03-26
相关资源
最近更新 更多