C ++ 0x unique_ptr заменяет scoped_ptr на владение?

Раньше я писал такой код:

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

person Neil G    schedule 11.06.2010    source источник
comment
Точно так же есть замена C ++ 0x для boost::scoped_array?   -  person rafak    schedule 11.06.2010
comment
@rafak std::unique_ptr также работает с массивами (он вызывает delete [])   -  person Cubbi    schedule 11.06.2010
comment
@rafak: используйте unique_ptr как это std::unique_ptr<P[]>. Не только unique_ptr вызовет delete [] при освобождении, но и отключает операторы * и - ›, а вместо этого предоставляет оператор [].   -  person deft_code    schedule 11.06.2010


Ответы (5)


Я поддержал ответ 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 позволяет настраивать создание и разрушение.

person Howard Hinnant    schedule 14.03.2011
comment
Хороший трюк! Я не думал об использовании каких-либо const ptr. Фактически, в некоторых случаях, когда я хочу запретить семантику копирования и перемещения, я все же хочу использовать семантику подкачки. Просто мне не нужны нулевые указатели - даже в условиях гонки. Будет ли разница между const scoped_ptr и const unique_ptr без использования семантики подкачки? - person comonad; 04.04.2011
comment
Единственное различие, которое я могу придумать между const scoped_ptr и const unique_ptr, заключается в том, что последняя позволяет использовать настраиваемый освободитель и настраиваемый тип указателя. Если вы хотите использовать своп, но не хотите перемещать или копировать, тогда scoped_ptr звучит лучше всего. Однако scoped_ptr допускает нулевые значения так же легко, как и unique_ptr. Возможно, вам понадобится умный указатель, который еще не обсуждался (и я не видел): not_null_ptr. - person Howard Hinnant; 04.04.2011
comment
scoped_ptr можно сбросить на другой указатель. const unique_ptr позволяет это? - person balki; 02.09.2012
comment
Неа. A const unique_ptr говорит: Я держу этот указатель от строительства до разрушения. - person Howard Hinnant; 03.09.2012

  • auto_ptr - это указатель с копией и с перемещение семантики и владения (= автоматическое удаление).
  • unique_ptr - это auto_ptr без копии, но с перемещением семантика.
  • scoped_ptr - это семантика auto_ptr без копирования и без перемещения.

    auto_ptr всегда плохой выбор - это очевидно.

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

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

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

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

Дополнительные сведения см. В разделе stackoverflow: smart-pointer-boost-separated.

person comonad    schedule 14.03.2011
comment
Я согласен с вами, и похоже, что вы согласны с ответом Джеймса. Спасибо, что нашли время ответить ... - person Neil G; 14.03.2011

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

person AraK    schedule 11.06.2010
comment
scoped_ptr не требует накладных расходов. Вы, наверное, думаете о shared_ptr :) - person Billy ONeal; 11.06.2010
comment
@Billy ONeal Иногда я не знаю, как что-то выразить по-английски :) По сути, я хотел сказать, что и то, и другое не создает накладных расходов. - person AraK; 11.06.2010
comment
Если они оба не создают накладных расходов, тогда почему один превосходит другого? (Ссылка - ›не связана с накладными расходами, как в случае с scoped_ptr, поэтому это превосходное средство) - person Billy ONeal; 11.06.2010

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

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

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

person James Michael Hare    schedule 24.06.2010
comment
Извините, но это абсолютно неверно. Самая большая проблема в том, что unique_ptr входит в стандарт, а scoped_ptr - нет. Следовательно, если для вашего приложения подходит компилятор C ++ 0x, вы всегда должны использовать unique_ptr. - person Billy ONeal; 10.09.2010
comment
@Billy Было бы лучше, если бы кто-то с твоим опытом был более любезен с новыми членами нашего сообщества, которые находят время, чтобы ответить на вопросы;) - person Neil G; 14.03.2011

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

Комментарий 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 следующим образом:

class A { // takes ownership A(unique_ptr

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

unique_ptr<P> p_;

};

person Dan    schedule 04.09.2011
comment
@Dan: В вашем примере owned_objects.push_back(rhs); не будет вызывать rvalue-версию push_back. Вам все равно нужно сказать owned_objects.push_back(std::move(rhs));, чтобы это произошло. rhs - это ссылка на rvalue (т. Е. Это тип переменной rhs), но выражение rhs дает lvalue (т. Е. Вы могли бы сказать rhs = something; или &rhs; и т. Д.). Это может сбивать с толку, но когда ссылочная переменная rvalue упоминается как выражение, результатом является lvalue. - person Karu; 10.02.2012