C ++ 0x unique_ptr заменяет scoped_ptr, вступая во владение? - PullRequest
19 голосов
/ 11 июня 2010

Я писал такой код:

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 ]

42 голосов
/ 14 марта 2011

Я проголосовал за ответ комонада, но с оговоркой:

Всякий раз, когда вы хотите явно disallow семантика перемещения, используйте scoped_ptr const unique_ptr .

Я не сталкивался ни с какими случаями использования, где const std::unique_ptr уступает boost::scoped_ptr. Однако я открыт для обучения по этому предмету.

Edit:

Вот пример использования 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 позволяет настраивать конструкцию и разрушение.

30 голосов
/ 14 марта 2011
  • A auto_ptr - указатель с , копия и с move семантика и владение (= автоудаление).
  • A unique_ptr - это auto_ptr без копирования , но с перемещение семантика.
  • A scoped_ptr является auto_ptr без копии и без движения семантика.

    auto_ptr всегда имеют плохой выбор - это

    Когда вы хотите явно иметь семантику перемещения, используйте unique_ptr .

    Всякий раз, когда вы хотитеявно запретить семантику перемещения, используйте scoped_ptr .

  • Все указатели позволяют swap семантикакак p.swap(q).Чтобы запретить их, используйте любой const… _ptr .

Существуют ситуации, когда вы хотите использовать scoped_ptr , указывающий на один из несколькихвзаимозаменяемые объекты: из-за отсутствия семантики перемещения вполне безопасно (в отношении очевидных ошибок), что оно не будет случайно указывать на ноль из-за непреднамеренного перемещения.Стоит упомянуть: scoped_ptr может по-прежнему эффективно swap ‍.Чтобы сделать его подвижным и / или копируемым - но все же с этой семантикой подкачки - вы можете рассмотреть возможность использования shared_ptr , указывающего на scoped_ptr , указывающего на обменный (через scoped_ptr :: swap) объект.

См. stackoverflow: объяснение smart-pointers-boost-объяснено для получения дополнительной информации.

2 голосов
/ 24 июня 2010

Я должен не согласиться с Араком в том, что кто-то выше. Не существует такой вещи, как превосходный выбор между двумя, поскольку это часто зависит от использования. Это все равно что сказать, что SmartCar превосходит пикап для всех целей, потому что он легче и быстрее. В действительности, иногда вам нужен грузовик, а иногда нет. Ваш выбор указателя должен основываться на том, что вам нужно.

Хорошая вещь в scoped_ptr - это уровень безопасности. Используя scoped_ptr, вы полагаете, что созданная память будет существовать только для этой области и не более, поэтому вы получаете защиту во время компиляции от попыток ее перемещения или передачи.

Итак, если вы хотите создать что-то, но ограничить его область, используйте scoped_ptr. Если вы хотите создать что-то и иметь право собственности, используйте unique_ptr. Если вы хотите создать что-то и поделиться этим указателем и очистить, когда все ссылки отсутствуют, используйте shared_ptr.

2 голосов
/ 11 июня 2010

IMO, лучше использовать unique_ptr, поскольку он предоставляет дополнительную функцию: семантика перемещения . то есть вы можете написать конструктор перемещения и т. д. для своего класса, в отличие от scoped_ptr. Кроме того, unique_ptr не связан с накладными расходами, как в случае с scoped_ptr, так что это превосходное средство. Решение о переписывании, конечно же, остается за вами, если вам не нужна семантика перемещения, тогда нет смысла переписывать. Не забывайте, что unique_ptr из стандартной библиотеки, поэтому она должна быть предоставлена ​​с любой совместимой реализацией C ++ 0x (когда она, конечно, станет реальностью)!

1 голос
/ 05 сентября 2011

Edit: мой плохой, вам действительно нужно написать move(p) внутри инициализатора. std::move обрабатывает все, что указано в качестве ссылки на rvalue, и в вашем случае, даже если ваш аргумент является ссылкой на rvalue на что-то, передача его чему-то другому (например, конструктору p_) передаст ссылку на lvalue, никогда Ссылка на значение по умолчанию.

Согласно комментарию Кару, также добавлены необходимые включения, чтобы сделать мой код компилируемым.

Например:

#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 (unique_ptr

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

unique_ptr<P> p_;
* * 1016};
...