shared_ptr 到数组:应该使用它吗?
只是关于 shared_ptr
的一个小问题.
使用 shared_ptr
指向数组是一个好习惯吗?例如,
shared_ptrsp(new int[10]);
如果没有,那为什么不呢?我已经知道的一个原因是不能增加/减少 shared_ptr
.因此它不能像普通的数组指针一样使用.
使用 C++17,shared_ptr
可用于管理动态分配的数组.在这种情况下,shared_ptr
模板参数必须是 T[N]
或 T[]
.所以你可以写
shared_ptrsp(new int[10]);
从 n4659,[util.smartptr.shared.const]
<块引用> 模板显式 shared_ptr(Y* p);
要求: Y
应该是一个完整的类型.表达式 delete[] p
,当 T
是数组类型时,或 delete p
,当 T
不是数组类型时一个数组类型,应该有明确的行为,并且不会抛出异常.
...
备注:当T
为数组类型时,此构造函数不参与重载解析,除非表达式delete[] p
格式正确且T
是 U[N]
并且 Y(*)[N]
可以转换为 T*
,或者T
是U[]
和 Y(*)[]
可转换为 T*
....
为了支持这一点,成员类型element_type
现在定义为
using element_type = remove_extent_t;
可以使用 operator 访问数组元素[]
element_type&运算符 [](ptrdiff_t i) 常量;
要求: get() != 0 &&我 >= 0
.如果 T
是 U[N]
,i <否
....
备注:当T
不是数组类型时,未指定是否声明该成员函数.如果声明了,则不指定其返回类型是什么,只是函数的声明(尽管不一定是定义)应格式良好.
<小时>
在 C++17 之前,shared_ptr
可以不能用于管理动态分配的数组.默认情况下,shared_ptr
将在托管对象上没有更多引用时调用 delete
.但是,当您使用 new[]
分配时,您需要调用 delete[]
而不是 delete
来释放资源.
为了在数组中正确使用 shared_ptr
,您必须提供自定义删除器.
模板<类型名称 T >struct array_deleter{void 运算符 ()( T const * p){删除[] p;}};
创建 shared_ptr 如下:
std::shared_ptrsp(new int[10], array_deleter());
现在 shared_ptr
将在销毁托管对象时正确调用 delete[]
.
上面的自定义删除器可以替换为
std::default_delete
数组类型的部分特化std::shared_ptr
sp(new int[10], std::default_delete ()); 一个 lambda 表达式
std::shared_ptr
sp(new int[10], [](int *p) { delete[] p; });
此外,除非您确实需要共享托管对象,否则 unique_ptr
更适合此任务,因为它对数组类型具有部分特化.
std::unique_ptr向上(新的 int[10]);//这将正确调用 delete[]
<小时>
库基础的 C++ 扩展引入的更改
库提供了另一种 C++17 之前的替代方案基础技术规范,增强了 shared_ptr
以允许它在拥有一组对象的情况下开箱即用.可以在 shared_ptr 更改的当前草案/n4082.pdf" rel="noreferrer">N4082.这些更改可通过 std::experimental
命名空间访问,并包含在
标头中.为数组支持 shared_ptr
的一些相关更改是:
—成员类型定义element_type
变化
typedef T element_type;
typedef typename remove_extent::type element_type;
—正在添加成员operator[]
element_type&运算符[](ptrdiff_t i) const noexcept;
—与数组的 unique_ptr
部分特化不同,shared_ptr
和 shared_ptr
都将有效并且两者都将导致在托管对象数组上调用 delete[]
.
模板显式 shared_ptr(Y* p);
要求:Y
应该是一个完整的类型.表达式 delete[] p
,当 T
是数组类型时,或 delete p
,当 T
不是数组类型时一个数组类型,应该是格式良好的,应该有明确定义的行为,并且不应该抛出异常.当T
为U[N]
时,Y(*)[N]
可转换为T*
;当T
为U[]
时,Y(*)[]
可转换为T*
;否则,Y*
应可转换为 T*
.
Just a small query regarding shared_ptr
.
Is it a good practice to use shared_ptr
pointing to an array? For example,
shared_ptr<int> sp(new int[10]);
If not, then why not? One reason I am already aware of is one can not increment/decrement the shared_ptr
. Hence it can not be used like a normal pointer to an array.
With C++17, shared_ptr
can be used to manage a dynamically allocated array. The shared_ptr
template argument in this case must be T[N]
or T[]
. So you may write
shared_ptr<int[]> sp(new int[10]);
From n4659, [util.smartptr.shared.const]
template<class Y> explicit shared_ptr(Y* p);
Requires:
Y
shall be a complete type. The expressiondelete[] p
, whenT
is an array type, ordelete p
, whenT
is not an array type, shall have well-defined behavior, and shall not throw exceptions.
...
Remarks: WhenT
is an array type, this constructor shall not participate in overload resolution unless the expressiondelete[] p
is well-formed and eitherT
isU[N]
andY(*)[N]
is convertible toT*
, orT
isU[]
andY(*)[]
is convertible toT*
. ...
To support this, the member type element_type
is now defined as
using element_type = remove_extent_t<T>;
Array elements can be access using operator[]
element_type& operator[](ptrdiff_t i) const;
Requires:
get() != 0 && i >= 0
. IfT
isU[N]
,i < N
. ...
Remarks: WhenT
is not an array type, it is unspecified whether this member function is declared. If it is declared, it is unspecified what its return type is, except that the declaration (although not necessarily the definition) of the function shall be well formed.
Prior to C++17, shared_ptr
could not be used to manage dynamically allocated arrays. By default, shared_ptr
will call delete
on the managed object when no more references remain to it. However, when you allocate using new[]
you need to call delete[]
, and not delete
, to free the resource.
In order to correctly use shared_ptr
with an array, you must supply a custom deleter.
template< typename T >
struct array_deleter
{
void operator ()( T const * p)
{
delete[] p;
}
};
Create the shared_ptr as follows:
std::shared_ptr<int> sp(new int[10], array_deleter<int>());
Now shared_ptr
will correctly call delete[]
when destroying the managed object.
The custom deleter above may be replaced by
the
std::default_delete
partial specialization for array typesstd::shared_ptr<int> sp(new int[10], std::default_delete<int[]>());
a lambda expression
std::shared_ptr<int> sp(new int[10], [](int *p) { delete[] p; });
Also, unless you actually need share onwership of the managed object, a unique_ptr
is better suited for this task, since it has a partial specialization for array types.
std::unique_ptr<int[]> up(new int[10]); // this will correctly call delete[]
Changes introduced by the C++ Extensions for Library Fundamentals
Another pre-C++17 alternative to the ones listed above was provided by the Library Fundamentals Technical Specification, which augmented shared_ptr
to allow it to work out of the box for the cases when it owns an array of objects. The current draft of the shared_ptr
changes slated for this TS can be found in N4082. These changes will be accessible via the std::experimental
namespace, and included in the <experimental/memory>
header. A few of the relevant changes to support shared_ptr
for arrays are:
— The definition of the member type element_type
changes
typedef T element_type;typedef typename remove_extent<T>::type element_type;
— Member operator[]
is being added
element_type& operator[](ptrdiff_t i) const noexcept;
— Unlike the unique_ptr
partial specialization for arrays, both shared_ptr<T[]>
and shared_ptr<T[N]>
will be valid and both will result in delete[]
being called on the managed array of objects.
template<class Y> explicit shared_ptr(Y* p);
Requires:
Y
shall be a complete type. The expressiondelete[] p
, whenT
is an array type, ordelete p
, whenT
is not an array type, shall be well-formed, shall have well defined behavior, and shall not throw exceptions. WhenT
isU[N]
,Y(*)[N]
shall be convertible toT*
; whenT
isU[]
,Y(*)[]
shall be convertible toT*
; otherwise,Y*
shall be convertible toT*
.
相关文章