C ++ Zero of Zero:多态删除和unique_ptr行为

时间:2014-04-08 03:38:28

标签: c++ polymorphism smart-pointers rule-of-zero

强制执行零规则主题下的最近overload journal中,作者描述了我们如何避免编写五个规则的运算符,因为编写它们的原因是:

  1. 资源管理
  2. 多态删除
  3. 使用智能指针可以解决这两个问题。

    这里我对第二部分特别感兴趣。

    请考虑以下代码段:

    class Base
    {
    public:
        virtual void Fun() = 0;
    };
    
    
    class Derived : public Base
    {
    public:
    
        ~Derived()
        {
            cout << "Derived::~Derived\n";
        }
    
        void Fun()
        {
            cout << "Derived::Fun\n";
        }
    };
    
    
    int main()
    {
        shared_ptr<Base> pB = make_shared<Derived>();
        pB->Fun();
    }
    

    在这种情况下,正如本文作者所解释的那样,我们通过使用共享指针获得了多态删除,这确实有效。

    但如果我用shared_ptr替换unique_ptr,我将无法再观察到多态删除。

    现在我的问题是,为什么这两种行为有所不同?为什么shared_ptr处理多态删除而unique_ptr没有?

3 个答案:

答案 0 :(得分:3)

如果您使用C ++ 14 make_unique或在Yakk的答案中编写您自己的一个,它将会起作用。基本上,共享指针行为之间的区别在于:

template<
    class T,
    class Deleter = std::default_delete<T>
> class unique_ptr;

对于unique_pointer,正如您所看到的,删除器属于该类型。如果您声明unique_pointer<Base>,则始终使用std::default_delete<Base>作为默认值。但是make_unique将负责为您的班级使用正确的删除器。

使用shared_ptr时:

template< class Y, class Deleter >
shared_ptr( Y* ptr, Deleter d );

和其他重载作为构造函数。正如您所看到的,unique_ptr的默认删除器在声明类型时取决于模板参数(除非您使用make_unique),而对于shared_ptr,删除器取决于传递给构造函数的类型。


您可以看到一个版本,允许在没有虚拟析构函数here的情况下进行多态删除(此版本也适用于VS2012)。请注意,它有点被黑客攻击,我目前还不确定C ++ 14中unique_ptrmake_shared的行为是什么样的,但我希望它们能让这更容易。也许我会查看有关C ++ 14新增内容的论文,看看如果我以后有空的话会有什么变化。

答案 1 :(得分:3)

您的回答是:https://stackoverflow.com/a/22861890/2007142

引用:

  

一旦最后一个引用shared_ptr超出范围或被重置,将调用~Derived()并释放内存。因此,您不需要使~Base()虚拟。 unique_ptr<Base>make_unique<Derived>不提供此功能,因为他们没有提供shared_ptr关于删除器的机制,因为唯一指针更简单,目标是最低开销因此不存储删除器所需的额外函数指针。

答案 2 :(得分:1)

template<typename T>
using smart_unique_ptr=std::unique_ptr<T,void(*)(void*)>;

template<class T, class...Args> smart_unique_ptr<T> make_smart_unique(Args&&...args) {
  return {new T(std::forward<Args>(args)...), [](void*t){delete (T*)t;}};
}

问题是unique_ptr的默认删除器会在存储的指针上调用delete。上面存储了一个知道构造类型的删除器,因此当复制到基类unique_ptr时仍将作为子类删除。

这增加了适度的开销,因为我们必须取消引用指针。此外,它会对类型进行非规范化,因为默认构造的smart_unique_ptr现在是非法的。您可以通过一些额外的工作来解决这个问题(用至少不会崩溃的半智能仿函数替换原始函数指针:但是,如果unique非空时,函数指针应该断言存在调用删除器。)