我曾经写过这样的代码:
class P {};
class Q: public P {};
class A {
// takes ownership
A(P* p): p_(p) {}
scoped_ptr<P> p_;
};
A a(new Q);
使用C ++ 0x,我应该将A类重写为:
class A {
// takes ownership
A(unique_ptr<P>&& p): p_(p) {}
unique_ptr<P> p_;
};
答案 0 :(得分:42)
我赞成了comonad的回答,但有一点需要注意:
每当你想明确表达时 禁止 移动语义,使用
scoped_ptrconst unique_ptr 。
我没有遇到const std::unique_ptr
低于boost::scoped_ptr
的任何用例。但是我对这个问题的教育持开放态度。
编辑:
以下是我认为应该失败的boost::scoped_ptr
的用例,但事实并非如此。对于std::unique_ptr
:
#include <iostream>
#ifdef USE_UNIQUEPTR
#include <memory>
typedef std::unique_ptr<int> P;
#else // USE_UNIQUEPTR
#include <boost/scoped_ptr.hpp>
typedef boost::scoped_ptr<int> P;
#endif // USE_UNIQUEPTR
int main()
{
P p1(new int(1));
{
// new scope
#ifdef USE_UNIQUEPTR
const P p2(new int(2));
#else // USE_UNIQUEPTR
P p2(new int(2));
#endif // USE_UNIQUEPTR
swap(p1, p2); // should fail!
}
std::cout << *p1 << '\n';
}
如果boost::scoped_ptr
的承诺是其资源不会逃避当前范围,那么将该承诺保持为const std::unique_ptr
就不那么好了。如果我们想将const boost :: scoped_ptr与const :: std :: unique_ptr进行比较,我不得不问:出于什么目的?它们对我来说似乎是一样的,除了const std :: unique_ptr允许自定义构造和销毁。
答案 1 :(得分:30)
scoped_ptr 是 auto_ptr ,没有复制和没有移动语义。
auto_ptr s总是 错误选择 - 这很明显。
如果您想明确 移动语义,请使用 unique_ptr 。
如果您想明确 禁止 移动语义,请使用 scoped_ptr 。
所有指针都允许 swap 语义,例如p.swap(q)
。要禁止这些,请使用任何 const ... _ptr 。
在某些情况下,您希望使用 scoped_ptr 指向多个可互换对象中的一个:由于缺少移动语义,因此它非常安全(就明显的错误而言)由于意外移动,不会意外地指向null。值得一提的是: scoped_ptr s仍然可以有效地交换。要使它可移动和/或可复制 - 但仍然使用这些交换语义 - 您可能需要考虑使用 shared_ptr 指向指向可交换的 scoped_ptr (通过scoped_ptr: :swap)对象。
有关详细信息,请参阅stackoverflow:smart-pointers-boost-explained。
答案 2 :(得分:2)
IMO最好使用unique_ptr
,因为它提供了一个额外的功能:移动语义。即,您可以为您的班级编写移动构造函数等,与scoped_ptr
不同。此外,unique_ptr
没有与scoped_ptr
相关联的开销,因此它是一个优越的工具。当然,重写的决定取决于你,如果你不需要移动语义,那么重写是没有意义的。不要忘记unique_ptr
来自标准库,因此它必须提供C ++ 0x的任何兼容实现(当然它变为现实:)!
答案 3 :(得分:2)
我不得不同意AraK的优越感。两者之间没有优越的选择,因为它往往取决于使用情况。这就像说SmartCar在所有用途上都优于皮卡车,因为它更轻,更快。实际上,有时候你需要一辆卡车,有时你却不需要。您选择的指针应该基于您的需求。
关于scoped_ptr的好处是它增加了一定程度的安全性。通过使用scoped_ptr,您可以确认创建的内存仅存在于该范围内,而不再存在,因此您可以获得编译时保护,防止尝试移动或传输它。
因此,如果你想创建一些但是限制它的范围,则使用scoped_ptr。如果要创建某些内容并使所有权可以移动,请使用unique_ptr。如果你想创建一些东西并在所有参考文献消失时共享该指针和清理,请使用shared_ptr。
答案 4 :(得分:1)
编辑:我的不好,你需要在初始化程序中写move(p)
。 std::move
将它给出的任何内容视为右值引用,在您的情况下,即使您的参数是对某些内容的右值引用,将其传递给其他内容(如p_
的构造函数)也会传递左值引用,默认情况下永远不是右值引用。
Per Karu的评论,还添加了必要的包括使我的代码可编辑。
例如:
#include <memory>
#include <cassert>
#include <vector>
using namespace std;
class A {};
class B {
public:
void takeOwnershipOf(unique_ptr<A>&& rhs) {
// We need to explicitly cast rhs to an rvalue when passing it to push_back
// (otherwise it would be passed as an lvalue by default, no matter what
// qualifier it has in the argument list). When we do that, the move
// constructor of unique_ptr will take ownership of the pointed-to value
// inside rhs, thus making rhs point to nothing.
owned_objects.push_back(std::move(rhs));
}
private:
vector<unique_ptr<A>> owned_objects;
};
int main() {
unique_ptr<B> b(new B());
// we don't need to use std::move here, because the argument is an rvalue,
// so it will automatically be transformed into an rvalue reference.
b->takeOwnershipOf( unique_ptr<A>(new A()) );
unique_ptr<A> a (new A());
// a points to something
assert(a);
// however, here a is an lvalue (it can be assigned to). Thus we must use
// std::move to convert a into an rvalue reference.
b->takeOwnershipOf( std::move(a) );
// whatever a pointed to has now been moved; a doesn't own it anymore, so
// a points to 0.
assert(!a);
return 0;
}
另外,在您的原始示例中,您应该像这样重写A类:
A级{ //取得所有权 A(unique_ptr
&amp;&amp; p):p_(std :: move(p)){}
unique_ptr<P> p_;
};