Почему auto_ptr устарел?

Ответы:

93

Прямая замена auto_ptr(или, по крайней мере, самая близкая к ней) есть unique_ptr. Что касается «проблемы», все довольно просто: auto_ptrпередает право владения, когда оно назначено. unique_ptrтакже передает право собственности, но благодаря кодификации семантики перемещения и магии ссылок rvalue это может происходить значительно более естественно. Он также значительно лучше «вписывается» в остальную часть стандартной библиотеки (хотя, честно говоря, отчасти это произошло благодаря тому, что остальная часть библиотеки изменилась с учетом семантики перемещения, а не всегда требовала копирования).

Изменение имени также (ИМО) приветствуется - на auto_ptrсамом деле мало что говорит вам о том, что оно пытается автоматизировать, тогда unique_ptrкак это довольно разумное (если краткое) описание того, что предоставляется.

Джерри Гроб
источник
25
Просто обратите внимание на auto_ptrназвание: auto предлагает автоматический, как в автоматической переменной, и относится к одной вещи, которая auto_ptrделает: уничтожает управляемый ресурс в его деструкторе (когда он выходит за пределы области видимости).
Винченцо Пии
14
Дополнительная информация: Вот официальное обоснование отказа от поддержкиauto_ptr : open-std.org/jtc1/sc22/wg21/docs/papers/2005/…
Ховард Хиннант,
@HowardHinnant интересный документ! в некотором смысле странно, что если std :: sort () имеет специализацию для std :: unique_ptr, чтобы использовать семантику перемещения по мере необходимости. Интересно, почему std :: sort () не может быть специализирован для std :: auto_ptr, чтобы исправить проблему копирования, упомянутую в документе. Заранее спасибо.
Hei
2
@Hei: std::sortне имеет специализации unique_ptr. Вместо этого было изменено указание никогда не копировать. Так на auto_ptrсамом деле делает работу с современным sort. Но C ++ 98/03 sortявляется здесь просто примером алгоритма: любой общий алгоритм (предоставленный стандартным или написанным пользователем), который предполагает, что синтаксис копирования имеет семантику копирования, вероятно, будет иметь ошибку времени выполнения, если используется с auto_ptr, потому что перемещаетсяauto_ptr незаметно с копией синтаксиса. Проблема гораздо шире, чем просто . sort
Ховард
36

Я нашел существующие ответы отличными, но из PoV указателей. ИМО, идеальный ответ должен иметь ответ с точки зрения пользователя / программиста.

Первым делом (как указал Джерри Коффин в своем ответе)

  • auto_ptr можно заменить на shared_ptr или unique_ptr в зависимости от ситуации

shared_ptr: если вас беспокоит освобождение ресурса / памяти И если у вас есть несколько функций, которые могут использовать объект AT-DIFFERENT раз, тогда используйте shared_ptr.

В DIFFERENT-Times представьте себе ситуацию, когда объект-ptr хранится в нескольких структурах данных, а затем к нему осуществляется доступ. Другой пример - это, конечно, несколько потоков.

unique_ptr: если все, что вас беспокоит, - это освобождение памяти, а доступ к объекту - ПОСЛЕДОВАТЕЛЬНЫЙ, выберите unique_ptr.

Под ПОСЛЕДОВАТЕЛЬНОМ я подразумеваю, что в любой момент объект будет доступен из одного контекста. Например, объект, который был создан и использовался сразу после создания создателем. После создания объект сохраняется в ПЕРВОЙ структуре данных. Затем объект либо уничтожается после ОДНОЙ структуры данных, либо перемещается во ВТОРОЙ структуру данных.

В этой строке я буду называть общий / уникальный _ptr интеллектуальными указателями. (auto_ptr также является умным указателем, НО из-за недостатков в его конструкции, из-за которых они устарели и на которые, я думаю, я укажу в следующих строках, их не следует группировать с помощью умного указателя.)

Единственная наиболее важная причина того, почему auto_ptr устарел в пользу smart-указателя, - это семантика присваивания. Если бы не по этой причине, они бы добавили все новые полезности семантики перемещения в auto_ptr вместо того, чтобы исключать ее. Поскольку семантика присваивания была наиболее нежелательной функцией, они хотели, чтобы эта функция исчезла, но поскольку существует написанный код, использующий эту семантику (который комитет по стандартам не может изменить), им пришлось отказаться от auto_ptr вместо модифицируя его.

По ссылке: http://www.cplusplus.com/reference/memory/unique_ptr/operator=/

Вид присваиваний, поддерживаемых unqiue_ptr

  • переместить задание (1)
  • присвоить нулевой указатель (2)
  • присвоение типов (3)
  • копировать присвоение (удалено!) (4)

От: http://www.cplusplus.com/reference/memory/auto_ptr/operator=/

Вид присваиваний, поддерживаемых auto_ptr

  • копирование присвоения (4) виновник

Теперь, когда я перехожу к причине, ПОЧЕМУ само назначение копий так не понравилось, у меня есть следующая теория:

  1. Не все программисты читают книги или стандарты
  2. auto_ptr на первый взгляд обещает вам право собственности на объект
  3. маленький- * (каламбур), предложение auto_ptr, которое не читается всеми программистами, позволяет назначать один auto_ptr другому и передавать право владения.
  4. Исследования показали, что такое поведение предназначено для 3,1415926535% всего использования и непреднамеренно в других случаях.

Непредвиденное поведение действительно не нравится и, следовательно, неприязнь к auto_ptr.

(Для 3,1415926536% программистов, которые намеренно хотят передать право собственности, C ++ 11 дал им std :: move (), что сделало их намерение совершенно ясным для всех стажеров, которые собираются читать и поддерживать код.)

Аджит Ганга
источник
1
Поскольку вам никогда не нужно, чтобы два auto_ptrзначения указывали на один и тот же объект (поскольку они не предоставляют совместного владения, первое, что умрет, оставит другое со смертельным наследием; это также верно для unique_ptrиспользования), можете ли вы предложить, что было предназначено в оставшиеся 96,8584073465% всего использования?
Marc van Leeuwen
Не могу говорить за всех, но я предполагаю, что они подумают, что права собственности на объект перемещаются, а НЕ просто дублируются, что ошибочно.
Аджит Ганга,
@AjeetGanga В следующей фразе "маленький- * (каламбур)" вы упомянули "каламбур". Эта фраза для меня нова, но я все равно погуглил и узнал, что здесь есть какая-то шутка, которая была здесь специально сделана. Что это за шутка? Просто любопытно это узнать.
VINOTH ENERGETIC
@AjeetGanga Вы упомянули вроде «маленький- * (каламбур), пункт auto_ptr, который не читается всеми программистами, позволяет назначать один auto_ptr другому и передавать право собственности». Скажем, у меня есть два auto ptr как a и b для целого числа. Я выполняю задание, поскольку *a=*b;здесь только значение b копируется в a. Я надеюсь, что и a, и b по-прежнему принадлежат одним и тем же людям. Вы упомянули, что собственность будет передана. Как это будет?
VINOTH ENERGETIC
@VINOTHENERGETIC Аджит говорил о назначении самому auto_ptrобъекту. Присвоение / от указанной стоимости не влияет на право собственности и не имеет отношения к ней. Надеюсь, вы еще не пользуетесь auto_ptr?
underscore_d
23

shared_ptrможно хранить в контейнерах. auto_ptrне могу.

Кстати, unique_ptrэто действительно прямая auto_ptrзамена, она сочетает в себе лучшие черты обоих std::auto_ptrи boost::scoped_ptr.

Бен Фойгт
источник
11

Еще один подход к объяснению разницы ....

Функционально C ++ 11 std::unique_ptrявляется «фиксированным» std::auto_ptr: оба они подходят, когда - в любой момент времени во время выполнения - должен быть один владелец смарт-указателя для объекта, на который указывает.

Ключевое различие заключается в создании копирования или присваивании из другого умного указателя с истекшим сроком действия, что показано в =>строках ниже:

   std::auto_ptr<T> ap(...);
   std::auto_ptr<T> ap2(get_ap_to_T());   // take expiring ownership
=> std::auto_ptr<T> ap3(ap);  // take un-expiring ownership ala ap3(ap.release());
   ap->xyz;  // oops... can still try to use ap, expecting it to be non-NULL

   std::unique_ptr<T> up(...);
   std::unique_ptr<T> up2(get_up_to_T());   // take expiring ownership
=> std::unique_ptr<T> up3(up);  // COMPILE ERROR: can't take un-expiring ownership
=> std::unique_ptr<T> up4(std::move(up));  // EXPLICIT code allowed
=> std::unique_ptr<T> up4(up.release());   // EXPLICIT code allowed

Выше он ap3незаметно «крадет» право собственности *ap, оставляя apнабор на a nullptr, и проблема в том, что это может произойти слишком легко, если программист не продумал его безопасность.

Например, если у class/ structесть std::auto_ptrчлен, то при создании копии экземпляра будет releaseуказатель из копируемого экземпляра: это странная и опасно запутывающая семантика, поскольку обычно копирование чего-либо не меняет его. Автору класса / структуры легко не заметить освобождение указателя, рассуждая об инвариантах и ​​состоянии, и, следовательно, случайно попытаться разыменовать интеллектуальный указатель, пока он равен нулю, или просто еще не ожидает доступа / владения указанными данными.

Тони Делрой
источник
auto_ptr незаметно «ворует» собственность +1
camino
3

auto_ptr нельзя использовать в контейнерах STL, потому что у него есть конструктор копирования, который не соответствует требованиям контейнера CopyConstructible . unique_ptr не реализует конструктор копирования, поэтому контейнеры используют альтернативные методы. unique_ptr может использоваться в контейнерах и быстрее для алгоритмов std, чем shared_ptr.

#include <iostream>
#include <type_traits>
#include <vector>
#include <memory>

using namespace std;

int main() {
  cout << boolalpha;
  cout << "is_copy_constructible:" << endl;
  cout << "auto_ptr: " << is_copy_constructible< auto_ptr<int> >::value << endl;
  cout << "unique_ptr: " << is_copy_constructible< unique_ptr<int> >::value << endl;
  cout << "shared_ptr: " << is_copy_constructible< shared_ptr<int> >::value << endl;

  vector<int> i_v;
  i_v.push_back(1);
  cout << "i_v=" << i_v[0] << endl;
  vector<int> i_v2=i_v;
  cout << "i_v2=" << i_v2[0] << endl;

  vector< unique_ptr<int> > u_v;
  u_v.push_back(unique_ptr<int>(new int(2)));
  cout << "u_v=" << *u_v[0] << endl;
  //vector< unique_ptr<int> > u_v2=u_v;  //will not compile, need is_copy_constructible == true
  vector< unique_ptr<int> > u_v2 =std::move(u_v);  // but can be moved
  cout << "u_v2=" << *u_v2[0] << " length u_v: " <<u_v.size() << endl;

  vector< shared_ptr<int> > s_v;
  shared_ptr<int> s(new int(3));
  s_v.push_back(s);
  cout << "s_v=" << *s_v[0] << endl;
  vector< shared_ptr<int> > s_v2=s_v;
  cout << "s_v2=" << *s_v2[0] << endl;

  vector< auto_ptr<int> > a_v;  //USAGE ERROR

  return 0;
}

>cxx test1.cpp -o test1
test1.cpp: In function âint main()â:
test1.cpp:33:11: warning: âauto_ptrâ is deprecated (declared at /apps/hermes/sw/gcc/gcc-4.8.5/include/c++/4.8.5/backward/auto_ptr.h:87) [-Wdeprecated-declarations]
   vector< auto_ptr<int> > a_v;  //USAGE ERROR
           ^
>./test1
is_copy_constructible:
auto_ptr: false
unique_ptr: false
shared_ptr: true
i_v=1
i_v2=1
u_v=2
s_v=3
s_v2=3
edW
источник