【问题标题】:C++0x unique_ptr replaces scoped_ptr taking ownership?C++0x unique_ptr 替换 scoped_ptr 取得所有权?
【发布时间】:2011-03-02 11:05:19
【问题描述】:

我以前写过这样的代码:

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

【问题讨论】:

  • 同样,boost::scoped_array 是否有 C++0x 替代品?
  • @rafak std::unique_ptr 也适用于数组(它将调用 delete[])
  • @rafak:像这样使用 unique_ptr std::unique_ptr&lt;P[]&gt;。 unique_ptr 不仅会在释放时调用 delete[],而且会禁用 * 和 -> 运算符,而是提供 [] 运算符。

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


【解决方案1】:

我赞成 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 允许自定义构造和销毁。

【讨论】:

  • 好招!我没有考虑使用任何 const ptr。实际上,在某些我想禁止复制和移动语义的情况下,我仍然想使用交换语义。只是我不想要任何空指针——即使在竞争条件下也是如此。在不允许交换语义的情况下,const scoped_ptr 和 const unique_ptr 之间会有什么区别吗?
  • 我能想到的 const scoped_ptr 和 const unique_ptr 之间的唯一区别是后者允许自定义释放器和自定义指针类型。如果您想使用交换,但又不想移动或复制,那么 scoped_ptr 听起来是最合适的。但是 scoped_ptr 允许 null 和 unique_ptr 一样容易。您可能想要一个尚未讨论过的智能指针(我还没有看到):not_null_ptr。
  • a scoped_ptr 可以重置为不同的指针。 const unique_ptr 允许吗?
  • 不。一个const unique_ptr 说:我持有这个指针,从构造到销毁。
【解决方案2】:
  • auto_ptr 是指针 with copywith 移动语义和所有权(=自动删除)。
  • unique_ptrauto_ptr 无副本 移动语义。
  • scoped_ptrauto_ptr 没有复制没有移动语义。

    auto_ptr总是一个糟糕的选择——这是显而易见的。

    当您想明确移动语义时,请使用 unique_ptr

    当您想明确禁止移动语义时,请使用 scoped_ptr

  • 所有指针都允许 swap 语义,例如 p.swap(q)。要禁止这些,请使用任何 const …_ptr

在某些情况下,您希望使用 scoped_ptr 指向几个可互换对象之一:由于缺少移动语义,它是非常安全的(对于明显的错误)不会因为意外移动而意外指向 null。值得一提的是:scoped_ptr‍s 仍然可以有效地swap‍ped。为了使其可移动和/或可复制——但仍然具有这些交换语义——您可能需要考虑使用指向 scoped_ptrshared_ptr 指向可交换对象(通过 scoped_ptr: :swap) 对象。

更多详情请参阅stackoverflow:smart-pointers-boost-explained

【讨论】:

  • 我同意你的观点,看起来你同意詹姆斯的回答。感谢您花时间回答...
【解决方案3】:

IMO 最好使用unique_ptr,因为它提供了一个附加功能:移动语义。也就是说,你可以为你的类编写一个移动构造函数等,这与scoped_ptr 不同。此外,unique_ptr 没有与 scoped_ptr 相关的开销,因此它是一个优越的设施。重写的决定当然取决于你,如果你不需要移动语义,那么重写就没有意义了。不要忘记unique_ptr 来自标准库,因此必须提供任何兼容的 C++0x 实现(当然当它成为现实时:)!

【讨论】:

  • scoped_ptr 不会产生开销。你可能在想shared_ptr :)
  • @Billy ONeal 有时我不知道如何用英语表达一些东西 :) 基本上,我想说的是,两者都不会产生开销。
  • 如果它们都不会产生开销,那么为什么一个“优于”另一个呢? (引用 -> “没有与 scoped_ptr 相关的开销,因此它是一个 superior 设施”)
【解决方案4】:

我不得不不同意 AraK 的观点。两者之间没有更好的选择,因为它通常取决于使用情况。这就像说 SmartCar 在所有用途上都优于皮卡车,因为它更轻、更快。实际上,有时您需要卡车,有时则不需要。您选择的指针应该基于您的需要。

scoped_ptr 的好处在于它增加了一定程度的安全性。通过使用 scoped_ptr,您是在声明创建的内存将仅存在于该范围内,不再存在,因此您可以获得编译时保护,防止尝试移动或传输它。

所以,如果你想创建一些东西但限制它的范围,使用 scoped_ptr。如果你想创建一些东西并且拥有可移动的所有权,请使用 unique_ptr。如果你想创建一些东西并在所有引用都消失后共享该指针和清理,请使用 shared_ptr。

【讨论】:

  • 抱歉,这是大错特错。最大的问题是unique_ptr 在标准中,而scoped_ptr 不在标准中。因此,如果 C++0x 编译器适合您的应用程序,则应始终使用 unique_ptr
  • @Billy 对我们社区的新成员花时间回答问题的人来说,有你经验的人可能会更好;)
【解决方案5】:

编辑:我的错,你需要在初始化程序中写move(p)std::move 将给出的任何内容视为右值引用,在您的情况下,即使您的参数是对某事物的右值引用,将其传递给其他事物(如 p_ 的构造函数)将传递左值引用,而不是默认为右值引用。

根据 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

&& p): p_(std::move(p)) {}

unique_ptr<P> p_;

};

【讨论】:

  • @Dan:在您的示例中,owned_objects.push_back(rhs); 不会调用push_back 的右值版本。你仍然需要说owned_objects.push_back(std::move(rhs)); 才能发生这种情况。 rhs 是一个右值引用(即,这是变量rhs 的类型),但表达式rhs 产生一个左值(即,您可以说rhs = something;&amp;rhs; 等)。这可能会造成混淆,但是当将右值引用变量作为表达式提及时,结果是左值。
猜你喜欢
  • 2013-01-17
  • 1970-01-01
  • 1970-01-01
  • 2016-01-30
  • 2012-10-11
  • 2014-07-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多