使用 std::unique_ptr<T>& 而不是 std::unique_ptr<T> 有什么优势吗?
Are there any advantages of using std::unique_ptr<T>& instead of std::unique_ptr<T>?
使用 std::unique_ptr<T>&
代替 std::unique_ptr<T>
有什么优势吗?例如,在函数参数中?
由于 std::unique_ptr
无法按设计复制,因此只能通过将指针的所有权传递给函数(即移动构造)来调用采用 std::unique_ptr<T>
的函数。调用代码将无法再访问指针。
使用 std::unique_ptr<T>&
,您允许函数 'see' 指针并访问其成员,但调用代码保留所有权,因此仍然可以自己使用它。
Herb Sutter 解释了将智能指针传递给具有所有可能变化的函数的所有利弊。请阅读文章以获取更多信息,它非常好:
简而言之,在没有引用的情况下将 unique_ptr
传递给函数是没有意义的,除非您的预期行为是放弃指针的所有权。!
std::unique_ptr 只能移动,因此如果您按值传递 unique_ptr 则您无法在函数调用后提取其内容,但如果您按引用传递则可以检索值。以下是相同的示例代码:
#include <iostream>
#include <memory>
void changeUniquePtrReference(std::unique_ptr<int>& upr)
{
*upr = 9;
}
void changeUniquePtrValue(std::unique_ptr<int> upv)
{
*upv = 10;
}
int main()
{
std::unique_ptr<int> p(new int);
*p =8;
std::cout<<"value of p is "<<*p<<std::endl;
changeUniquePtrReference(p);
std::cout<<"value of p is "<<*p<<std::endl;
changeUniquePtrValue(std::move(p));
std::cout<<"Memory deallocated so below line will crash.. "<<std::endl;
std::cout<<"value of p is "<<*p<<std::endl;
return 0;
}
这两个变体不同,适用于特定情况。
如果您的函数采用 std::unique_ptr<T>
,这意味着您获得了托管对象的所有权,并将删除它或将其保存在其他地方。
如果您取得 std::unique_ptr<T> &
,您不会取得所有权,但您可以更改指针指向的内容,例如通过将其重置为不同的对象。
如果您不打算更改指针本身,您只需传递一个 T&
或一个 const T&
,具体取决于您是否打算修改对象。传递 const std::unique_ptr<T> &
是没有用的,因为它不能让你比 T&
做更多的事情,缺点是潜在的额外间接寻址和对参数的不必要限制。
有几种不同的情况[=27=]
- 您想将所有权转移给函数
- 使用
std::unique_ptr<T>
- 您想允许函数修改指针
- 使用
std::unique_ptr<T> &
- 您想允许函数修改指针对象
- 使用
T &
,并在调用站点取消引用
- 如果指针可能为空,则使用
T *
并在调用站点调用 unique_ptr::get
- 您想允许函数观察指针对象
- 使用
const T &
,并在调用站点取消引用
- 如果指针可能为空,则使用
const T *
并在调用站点调用 unique_ptr::get
- 您想允许函数拥有指针对象的副本
- 使用
T
,并在调用站点取消引用
- 您对
unique_ptr<T>
的集合进行了范围循环(或 <algorithm>
调用),并且想要观察这些值,但没有 ranges::indirect
(or similar)
- 使用
auto &
,知道推断为std::unique_ptr<T> &
(如果集合是const
则为const std::unique_ptr<T> &
),所以要在循环中解引用正文
就我个人而言,我只看到一种情况,您 应该 传递对 unique_ptr 的引用:您正在调用的方法可能 内部 决定 删除或放弃对象。这应该是非常罕见的,因为所有者放弃了所有权的决定,老实说这不是很好的设计。
如果子对象总是夺走所有权或删除对象,那么参数类型应该是unique_ptr值并立即转移所有权。
如果子对象只是想访问指针内容,那么参数应该是一个raw指针。您可以为 const unique_ptr reference
提出一个论点,这将阻止您释放它,但这有什么意义呢?如果你觉得你需要一个超越原始的约定,你可以修饰那个原始指针类型名称,这样你就知道你不拥有它。
使用 std::unique_ptr<T>&
代替 std::unique_ptr<T>
有什么优势吗?例如,在函数参数中?
由于 std::unique_ptr
无法按设计复制,因此只能通过将指针的所有权传递给函数(即移动构造)来调用采用 std::unique_ptr<T>
的函数。调用代码将无法再访问指针。
使用 std::unique_ptr<T>&
,您允许函数 'see' 指针并访问其成员,但调用代码保留所有权,因此仍然可以自己使用它。
Herb Sutter 解释了将智能指针传递给具有所有可能变化的函数的所有利弊。请阅读文章以获取更多信息,它非常好:
简而言之,在没有引用的情况下将 unique_ptr
传递给函数是没有意义的,除非您的预期行为是放弃指针的所有权。!
std::unique_ptr 只能移动,因此如果您按值传递 unique_ptr 则您无法在函数调用后提取其内容,但如果您按引用传递则可以检索值。以下是相同的示例代码:
#include <iostream>
#include <memory>
void changeUniquePtrReference(std::unique_ptr<int>& upr)
{
*upr = 9;
}
void changeUniquePtrValue(std::unique_ptr<int> upv)
{
*upv = 10;
}
int main()
{
std::unique_ptr<int> p(new int);
*p =8;
std::cout<<"value of p is "<<*p<<std::endl;
changeUniquePtrReference(p);
std::cout<<"value of p is "<<*p<<std::endl;
changeUniquePtrValue(std::move(p));
std::cout<<"Memory deallocated so below line will crash.. "<<std::endl;
std::cout<<"value of p is "<<*p<<std::endl;
return 0;
}
这两个变体不同,适用于特定情况。
如果您的函数采用 std::unique_ptr<T>
,这意味着您获得了托管对象的所有权,并将删除它或将其保存在其他地方。
如果您取得 std::unique_ptr<T> &
,您不会取得所有权,但您可以更改指针指向的内容,例如通过将其重置为不同的对象。
如果您不打算更改指针本身,您只需传递一个 T&
或一个 const T&
,具体取决于您是否打算修改对象。传递 const std::unique_ptr<T> &
是没有用的,因为它不能让你比 T&
做更多的事情,缺点是潜在的额外间接寻址和对参数的不必要限制。
有几种不同的情况[=27=]
- 您想将所有权转移给函数
- 使用
std::unique_ptr<T>
- 使用
- 您想允许函数修改指针
- 使用
std::unique_ptr<T> &
- 使用
- 您想允许函数修改指针对象
- 使用
T &
,并在调用站点取消引用 - 如果指针可能为空,则使用
T *
并在调用站点调用unique_ptr::get
- 使用
- 您想允许函数观察指针对象
- 使用
const T &
,并在调用站点取消引用 - 如果指针可能为空,则使用
const T *
并在调用站点调用unique_ptr::get
- 使用
- 您想允许函数拥有指针对象的副本
- 使用
T
,并在调用站点取消引用
- 使用
- 您对
unique_ptr<T>
的集合进行了范围循环(或<algorithm>
调用),并且想要观察这些值,但没有ranges::indirect
(or similar)- 使用
auto &
,知道推断为std::unique_ptr<T> &
(如果集合是const
则为const std::unique_ptr<T> &
),所以要在循环中解引用正文
- 使用
就我个人而言,我只看到一种情况,您 应该 传递对 unique_ptr 的引用:您正在调用的方法可能 内部 决定 删除或放弃对象。这应该是非常罕见的,因为所有者放弃了所有权的决定,老实说这不是很好的设计。
如果子对象总是夺走所有权或删除对象,那么参数类型应该是unique_ptr值并立即转移所有权。
如果子对象只是想访问指针内容,那么参数应该是一个raw指针。您可以为 const unique_ptr reference
提出一个论点,这将阻止您释放它,但这有什么意义呢?如果你觉得你需要一个超越原始的约定,你可以修饰那个原始指针类型名称,这样你就知道你不拥有它。