如果函数 returns a std::unique_ptr,假设每个返回值都是一个新对象是否合理?
If a function returns a std::unique_ptr, is it reasonable to assume each returned value is to a new object?
我正在学习 std::unique_ptr
,试图理解它代表什么。
给定一个 returns 一个 unique_ptr
的函数(我无法控制),是否 implied/well 理解每个调用 returns 一个 unique_ptr
指向一个新对象(不同于任何先前的调用)?
举例来说,下面的代码在退出时产生双重释放,我希望我能正确理解原因:unique_ptr
s 在销毁时删除它们的底层对象;因此,两个 unique_ptr
封装相同的 memory/object 会导致在破坏第二个时出现双重释放。所以下面的函数实现getUniquePtr()
会不会被commonly/implicitly理解为不合理?
// main.cpp
#include <memory>
#include <iostream>
std::unique_ptr<int> getUniquePtr() {
static int* p = new int(42);
return std::unique_ptr<int>(p);
}
class PtrOwner {
public:
std::unique_ptr<int> p_;
};
int main( int argc, char* argv[] ) {
PtrOwner po1;
PtrOwner po2;
po1.p_ = getUniquePtr();
po2.p_ = getUniquePtr();
return 0;
}
应该假设,如果一个函数returns std::unique_ptr<T>
,那么returned智能指针指向一个当前未被管理的对象其他人。这并不一定意味着它总是指代不同的对象。只要遵循这个约定,就可以避免双重错误。如果违反这个约定,就会出现double-free的bug。
例如,如果您看到这样的函数:
std::unique_ptr<T> foo(std::unique_ptr<T> arg);
在某些情况下,此函数可能会 return std::move(arg)
或者它可能会破坏 arg
和 return 一些其他指针。 (您必须阅读文档才能知道它的作用)。这意味着您可以这样做:
auto t = std::make_unique<T>();
t = foo(std::move(t));
t = foo(std::move(t));
在这种情况下,foo
可能只是 return 相同的指针值两次,这是绝对安全的。这个例子看起来很愚蠢,但希望它能表达我的观点。
是的,您的假设(大部分)是正确的。 unique_ptr
独占该对象,这意味着在任何给定时间任何两个 unique_ptr
都不应指向同一个对象。
正如其他人所指出的,这并不能保证多次调用同一函数 return 的不同对象。这是因为你可以传递所有权(通过移动它),所以如果你将所有权传回一个函数,就像在 Brian 的回答中那样,它很可能再次 return 同一个对象,而不违反任何规则。
如果您决定使用智能指针来管理对象,请尽量避免使用 new 和 delete。要创建属于 unique_ptr
的对象 T,请使用 make_unique<T>
。这样就避免了为同一个对象创建多个unique_ptr
的错误。正是由于这个原因,unique_ptr
不可复制。换句话说,你的 getUniquePtr 应该这样实现:
return std::make_unique<int>(42);
从原始指针构造智能指针(与使用 make_unique 相对)的少数几个原因之一是当您有现有代码或使用原始指针的库时,您无法更改。
我正在学习 std::unique_ptr
,试图理解它代表什么。
给定一个 returns 一个 unique_ptr
的函数(我无法控制),是否 implied/well 理解每个调用 returns 一个 unique_ptr
指向一个新对象(不同于任何先前的调用)?
举例来说,下面的代码在退出时产生双重释放,我希望我能正确理解原因:unique_ptr
s 在销毁时删除它们的底层对象;因此,两个 unique_ptr
封装相同的 memory/object 会导致在破坏第二个时出现双重释放。所以下面的函数实现getUniquePtr()
会不会被commonly/implicitly理解为不合理?
// main.cpp
#include <memory>
#include <iostream>
std::unique_ptr<int> getUniquePtr() {
static int* p = new int(42);
return std::unique_ptr<int>(p);
}
class PtrOwner {
public:
std::unique_ptr<int> p_;
};
int main( int argc, char* argv[] ) {
PtrOwner po1;
PtrOwner po2;
po1.p_ = getUniquePtr();
po2.p_ = getUniquePtr();
return 0;
}
应该假设,如果一个函数returns std::unique_ptr<T>
,那么returned智能指针指向一个当前未被管理的对象其他人。这并不一定意味着它总是指代不同的对象。只要遵循这个约定,就可以避免双重错误。如果违反这个约定,就会出现double-free的bug。
例如,如果您看到这样的函数:
std::unique_ptr<T> foo(std::unique_ptr<T> arg);
在某些情况下,此函数可能会 return std::move(arg)
或者它可能会破坏 arg
和 return 一些其他指针。 (您必须阅读文档才能知道它的作用)。这意味着您可以这样做:
auto t = std::make_unique<T>();
t = foo(std::move(t));
t = foo(std::move(t));
在这种情况下,foo
可能只是 return 相同的指针值两次,这是绝对安全的。这个例子看起来很愚蠢,但希望它能表达我的观点。
是的,您的假设(大部分)是正确的。 unique_ptr
独占该对象,这意味着在任何给定时间任何两个 unique_ptr
都不应指向同一个对象。
正如其他人所指出的,这并不能保证多次调用同一函数 return 的不同对象。这是因为你可以传递所有权(通过移动它),所以如果你将所有权传回一个函数,就像在 Brian 的回答中那样,它很可能再次 return 同一个对象,而不违反任何规则。
如果您决定使用智能指针来管理对象,请尽量避免使用 new 和 delete。要创建属于 unique_ptr
的对象 T,请使用 make_unique<T>
。这样就避免了为同一个对象创建多个unique_ptr
的错误。正是由于这个原因,unique_ptr
不可复制。换句话说,你的 getUniquePtr 应该这样实现:
return std::make_unique<int>(42);
从原始指针构造智能指针(与使用 make_unique 相对)的少数几个原因之一是当您有现有代码或使用原始指针的库时,您无法更改。