在强制执行零规则主题下的最近overload journal中,作者描述了我们如何避免编写五个规则的运算符,因为编写它们的原因是:
使用智能指针可以解决这两个问题。
这里我对第二部分特别感兴趣。
请考虑以下代码段:
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
没有?
答案 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_ptr
和make_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
非空时,函数指针应该断言存在调用删除器。)