使用 C++17,shared_ptr 可用于管理动态分配的数组。在这种情况下,shared_ptr 模板参数必须是 T[N] 或 T[]。所以你可以写
shared_ptr<int[]> sp(new int[10]);
来自 n4659,[util.smartptr.shared.const]
template<class Y> explicit shared_ptr(Y* p);
要求: Y 应该是一个完整的类型。表达式delete[] p,当T 是数组类型时,或delete p,当T 不是数组类型时,应具有明确定义的行为,并且不应抛出异常。
...
备注: 当T 是数组类型时,此构造函数不应参与重载决议,除非表达式delete[] p 是格式正确的并且T 是@987654344 @ 和 Y(*)[N] 可转换为 T*,或者 T 是
U[] 和 Y(*)[] 可转换为 T*。 ...
为了支持这一点,成员类型element_type 现在定义为
using element_type = remove_extent_t<T>;
可以使用operator[]访问数组元素
element_type& operator[](ptrdiff_t i) const;
需要: get() != 0 && i >= 0。如果T 是U[N],i < N。
...
备注:当T不是数组类型时,是否声明该成员函数是未指定的。如果是声明的,它的返回类型是什么是未指定的,只是函数的声明(虽然不一定是定义)应该是格式良好的。
在 C++17 之前,shared_ptr 可能不能用于管理动态分配的数组。默认情况下,当不再有对托管对象的引用时,shared_ptr 将在托管对象上调用delete。但是,当您使用new[] 进行分配时,您需要调用delete[] 而不是delete 来释放资源。
为了正确使用 shared_ptr 和数组,您必须提供自定义删除器。
template< typename T >
struct array_deleter
{
void operator ()( T const * p)
{
delete[] p;
}
};
如下创建shared_ptr:
std::shared_ptr<int> sp(new int[10], array_deleter<int>());
现在shared_ptr 将在销毁托管对象时正确调用delete[]。
上面的自定义删除器可以替换为
-
std::default_delete 数组类型的部分特化
std::shared_ptr<int> sp(new int[10], std::default_delete<int[]>());
-
一个 lambda 表达式
std::shared_ptr<int> sp(new int[10], [](int *p) { delete[] p; });
此外,除非您确实需要托管对象的共享权限,否则 unique_ptr 更适合此任务,因为它对数组类型具有部分特化。
std::unique_ptr<int[]> up(new int[10]); // this will correctly call delete[]
C++ Extensions for Library Fundamentals 引入的变化
Library Fundamentals Technical Specification 提供了上面列出的另一个 C++17 之前的替代方案,它对shared_ptr 进行了扩充,使其能够在拥有对象数组的情况下开箱即用。可以在N4082 中找到为此 TS 计划的 shared_ptr 更改的当前草案。这些更改可通过std::experimental 命名空间访问,并包含在<experimental/memory> 标头中。支持数组的shared_ptr 的一些相关更改是:
——成员类型element_type的定义发生变化
typedef T element_type;
typedef typename remove_extent<T>::type element_type;
—正在添加成员operator[]
element_type& operator[](ptrdiff_t i) const noexcept;
— 与数组的部分特化 unique_ptr 不同,shared_ptr<T[]> 和 shared_ptr<T[N]> 都是有效的,并且都会导致在托管对象数组上调用 delete[]。
template<class Y> explicit shared_ptr(Y* p);
要求:Y 应该是一个完整的类型。表达式delete[] p,当T 是数组类型时,或delete p,当T 不是数组类型时,应具有良好的格式,应具有良好定义的行为,并且不应抛出异常。当T 为U[N] 时,Y(*)[N] 应可转换为T*;当T 为U[] 时,Y(*)[] 应可转换为T*;否则,Y* 应可转换为 T*。