为什么std::unique_ptr
包含删除函数签名作为模板定义的一部分,是否有理由?
template<class T, class Deleter = std::default_delete<T>> class unique_ptr;
VS
template< class T > class shared_ptr;
答案 0 :(得分:10)
unique_ptr
的一般目标是在唯一指针超出范围时提供指针的自动删除。当使用默认删除器时(只调用delete
),除了指针本身之外,unique_ptr
对象中不需要任何额外的数据成员。这意味着默认情况下,unique_ptr
几乎没有任何开销(因为大多数(如果不是全部)其功能都将被内联)。
但他们也希望能够在有意义的特殊情况下提供更改删除器的选项。提供该选项的唯一方法是,同时仍然能够优化它(存储和内联调用)是使其成为类型本身的静态部分,即通过模板参数。关键是unique_ptr
旨在成为原始指针的最小开销替代。
在shared_ptr
的情况下,目标是完全不同的,现有的开销也是如此。共享指针实际上使用共享存储(动态分配),它存储指针,引用计数和删除对象。换句话说,已经有很大的开销和适当的位置放置删除对象而不会导致额外的每指针开销。此外,利用所有引用计数机制,与现有开销相比,虚拟调用(执行删除)的开销相形见绌。这就是为什么在共享指针中包含删除删除对象的类型的方便功能的自然选择。
并且,如果要创建具有类型擦除删除器的唯一指针类型,则使用模板别名非常简单:
template <typename T>
using any_unique_ptr = std::unique_ptr< T, std::function< void(T*) > >;
或类似的东西,就像这个删除器一样:
template <typename T>
struct type_erased_delete {
std::function< void(T*) > f;
type_erased_delete() : f(std::default_delete<T>()) { };
template <typename Func>
type_erased_delete(Func&& aF) : f(std::forward<Func>(aF)) { };
void operator()(T* p) const { f(p); };
};
template <typename T>
using any_unique_ptr = std::unique_ptr< T, type_erased_delete<T> >;
答案 1 :(得分:3)
这意味着,如果删除器没有状态,unique_ptr
可以不大于普通指针;移动它并不比复制和归零指针贵。
为了能够处理多种类型的删除器,shared_ptr
可以,它需要使用类型删除。为此,每个unique_ptr
都需要包含一个指向删除器的指针(以多态方式调用它),以及指向托管对象的指针,使其大小加倍。
shared_ptr
已经有额外的开销,因为它需要一个指针来访问引用计数。删除器可以与引用计数一起存储。
答案 2 :(得分:3)
在shared_ptr
的情况下,它在删除器上使用类型擦除。执行该类型擦除会有一些额外的开销,unique_ptr
不需要。特别是,大多数解决方案都需要为任何功能对象删除器分配动态内存。鉴于unique_ptr
的目标,我可以理解为什么他们选择不使用该技术。
另一方面,shared_ptr
已经包含一些额外的引用计数开销,因为它已经需要动态分配引用计数。这意味着类型擦除的额外开销较小。那就是boost::shared_ptr
的编写方式,而标准化的版本基本上是对它的捕捉。
答案 3 :(得分:0)
可以使用new[]
构建 - 因此需要调用delete[]
- 而不是delete
。
该模板为syou提供了此选项以及日志记录等功能。