8

在最近的超载期刊中,主题为Enforcing the rule of zero,作者描述了我们如何避免编写五个运算符的规则,因为编写它们的原因是:

  1. 资源管理
  2. 多态性缺失

这两个都可以通过使用智能指针来处理。

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

考虑以下代码片段:

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用 a替换unique_ptr,我将不再能够观察到多态删除。

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

4

3 回答 3

4

你在这里有你的答案:https ://stackoverflow.com/a/22861890/2007142

引用:

一旦最后一个引用shared_ptr超出范围或被重置,~Derived()将被调用并释放内存。因此,您不需要进行~Base()虚拟化。unique_ptr<Base>并且make_unique<Derived>不提供此功能,因为它们不提供shared_ptr与删除器相关的机制,因为唯一指针要简单得多,并且旨在实现最低开销,因此不存储删除器所需的额外函数指针。

于 2014-04-08T05:03:39.367 回答
3

It'll work if you use the C++14 make_unique or write your own one like in Yakk's answer. Basically the difference between the shared pointer behavior is that you got:

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

for unique_pointer and as you can see, the deleter belongs to the type. If you declare a unique_pointer<Base> it'll always use std::default_delete<Base> as default. But make_unique will take care of using the correct deleter for your class.

When using shared_ptr you got:

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

and other overloads as constructor. As you can see the default deleter for unique_ptr depends on the template parameter when declaring the type (unless you use make_unique) whilst for shared_ptr the deleter depends on the type passed to the constructor.


You can see a version that allows polymorphic delete without virtual destructor here (this version should also work in VS2012). Note that it is quite a bit hacked together and I'm currently not sure what the behavior of unique_ptr and make_shared in C++14 will be like, but I hope they'll make this easier. Maybe I'll look into the papers for the C++14 additions and see if something changed if I got the time later.

于 2014-04-08T04:10:05.697 回答
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的 s 现在是非法的。您可以通过一些额外的工作来解决这个问题(用至少不会崩溃的半智能仿函数替换原始函数指针:但是,如果unique调用删除器时函数指针非空,则应该断言函数指针存在)。

于 2014-04-08T03:55:38.943 回答