使用shared_ptr和unique_ptr时的不完整类型

6
我想了解为什么unique_ptr析构函数在销毁时需要类型完成,而shared_ptr则不需要。Howard Hinnant的博客简要提到这与静态和动态删除器有关。我正在寻找更详细的解释,说明为什么会出现这种情况(如果是编译器特定实现,则示例将很有帮助)。对于动态删除器,是否限制了析构函数的内联?

shared_ptr 在构造时捕获了一个删除器,并将其存储在控制块中。例如,shared_ptr<Base> ptr(new Derived); 可以正常工作,并最终调用正确的析构函数 ~Derived,即使 Base 没有虚析构函数(因为构造函数本质上保存了 [dervied_ptr]() { delete dervied_ptr; },而析构函数调用相同的内容)。std::unique_ptr<T> 不会执行类似的操作;它的析构函数只是对该 unique_ptr 存储的类型为 T* 的原始指针 p 执行 delete p - Igor Tandetnik
@IgorTandetnik 我已经发布了一个跟进答案的帖子,但是在使用pImpl习惯时,使用shared_ptrunique_ptr有什么影响。具体来说,为什么不需要在cpp文件中定义包装类的析构函数,似乎与unique_ptr的析构函数内联有关,而这在shared_ptr中是不可能的,但我不明白其中的原因。 - user3882729
1
unique_ptr<T> needs T to be a complete type at the point where ~unique_ptr destructor is called. shared_ptr<T> needs T (or rather, the pointer passed to the constructor, which may e.g. be a class derived from T) to be complete at the point where its constructor is called. If you don't put the destructor of the wrapper class into the cpp file, it will be implicitly defined in the h file, and that implicit definition is going to call the destructor of the smart pointer, at the time pImpl class is incomplete. That's a problem for unique_ptr, not a problem for shared_ptr - Igor Tandetnik
2个回答

6

Howard Hinnant在简化。他确切的意思是如果您使用std::unique_ptr的默认删除器,您需要一个完整的类型。对于默认删除程序,它只是为您调用delete

静态删除器和动态删除器的要点是

class A;

void static_delete(A* p)
{
    delete p;
}

void (*dynamic_delete)(A*) = /* somehow */;

int main()
{
    A* p = /* somehow */;
    static_delete(p);  // undefined behaviour
    dynamic_delete(p); // maybe not
}

只要dynamic_delete指向在定义A的同一位置定义的函数,就可以得到良好定义的行为。
为了防止未定义的行为,缺省删除器会检查是否为完整类型,这可能被实现为:
void default_delete(A* p)
{
    static_assert(sizeof(A) > 0);
    delete p;
}

在应用于pImpl惯用法的shared_ptrunique_ptr的析构函数的上下文中,包装类的析构函数必须在cpp文件中定义,但如果使用shared_ptr则没有这样的限制。为什么会出现这种情况,即什么保证动态删除器将在实现类之后定义? - user3882729
如果不是这样,当您尝试创建dynamic_delete时,会出现编译错误。但这是另一个问题。重点是,在main中您不需要完整的类型。 - Passer By

1
我希望了解为什么unique_ptr的析构函数在销毁时需要类型完整,而shared_ptr则不需要。
我认为这很简单,因为默认的析构函数需要知道数据成员的大小。对于unique_ptr,删除器是对象表示的一部分,而对于shared_ptr,实际的删除器并不那么重要,因为它存储在控制块中,无论您使用何种删除器。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接