C ++ 0x unique_ptr取代scoped_ptr取得所有权?

时间:2010-06-11 00:49:53

标签: c++ smart-pointers c++11

我曾经写过这样的代码:

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_;
};

5 个答案:

答案 0 :(得分:42)

我赞成了comonad的回答,但有一点需要注意:

  

每当你想明确表达时    禁止 移动语义,使用 scoped_ptr const 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)

  • auto_ptr 是指针 复制 移动语义和所有权(=自动删除)。
  • unique_ptr auto_ptr 没有副本但是 移动语义。
  • 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_;

};