Чому неправильно використовувати std :: auto_ptr <> зі стандартними контейнерами?


217

Чому неправильно використовувати std::auto_ptr<>стандартні контейнери?


5
Безумовно, +1 з цього приводу, бо я бачив, як багато людей це неправильно сприймають. Це чудове запитання.
twokats

Будь ласка, прочитайте також відповідний пункт. Це питання розглядається тут з іншого боку. Може бути корисно дізнатися більше про контейнери auto_ptr та STL. stackoverflow.com/questions/8630552 / ...
Nickolay


1
moveсемантичні і unique_ptrбули розроблені, щоб уникнути проблем, пов'язаних з auto_ptr. У C ++ 03 мова була недостатньо потужною для написання такого класу, auto_ptrякий поводиться правильно та безпечно у всіх сценаріях, оскільки компілятор та мова не змогли розрізнити значення l та r, тому деякі "хакі" використовувались для отримання бажаної поведінки більшість часу.
Phil1970

Приємна стаття: STL контейнери та Auto_ptrs - чому вони не змішують quantstart.com/articles/…
alfC

Відповіді:


124

Стандарт C ++ говорить, що елемент STL повинен бути "конструювальним для копіювання" та "присвоюваним". Іншими словами, елемент повинен бути призначений або скопійований, і два елементи логічно незалежні. std::auto_ptrне виконує цю вимогу.

Візьмемо для прикладу цей код:

class X
{
};

std::vector<std::auto_ptr<X> > vecX;
vecX.push_back(new X);

std::auto_ptr<X> pX = vecX[0];  // vecX[0] is assigned NULL.

Щоб подолати це обмеження, слід використовувати std::unique_ptr, std::shared_ptrабо std::weak_ptrрозумні вказівники, або еквіваленти підсилення, якщо у вас немає C ++ 11. Ось документація до бібліотеки підвищення для цих розумних покажчиків.


7
Вам також слід розглянути контейнери покажчика підвищення, якщо вам не потрібне спільне володіння.
me22,

4
unique_ptrтакож забороняє копіювання, тому певні операції STL працюватимуть некоректно, якщо вони не зможуть використовувати його семантику переміщення.
Mike Weller

4
"Щоб подолати це обмеження, слід використовувати std::unique_ptr": шаблон цього класу може існувати лише завдяки семантиці переміщення (його специфікація вимагає посилань на rvalue), тому він принципово вимагає C ++ 11. Однак (і пов'язані з цим) стандарт C ++ 11 більше не говорить, що тип елемента STL повинен бути "конструюваним для копіювання" та "призначуваним"; достатньо бути конструктивними та переміщуваними. Дійсно, unique_ptrекземпляри можуть бути лише побудованими для переміщення та призначеними для переміщення. Але такі ж auto_ptrвипадки! Як наслідок, в C ++ 11 ви можете робити auto_ptrте, з чим можна unique_ptr.
Марк ван Левен

@MarcvanLeeuwen, якщо ти resetта, releaseякщо потрібно,
уродливий фрік

2
@ratchetfreak: Хм, я не розумію. Що? "якщо ти resetі release", я не бачу, як це стосується чогось у моєму коментарі. Зверніть увагу, що обидва auto_ptrі unique_ptrмають обидва ці методи, і вони роблять те саме в обох випадках.
Марк ван Левен

66

У копіювальної семантика з auto_ptrне сумісні з контейнерами.

Зокрема, копіювання одного auto_ptrв інший не створює двох рівних об’єктів, оскільки один втратив право власності на вказівник.

Більш конкретно, копіювання auto_ptrзаставляє одну з копій відпустити вказівник. Який із цих залишків у контейнері не визначено. Тому ви можете випадково втратити доступ до покажчиків, якщо зберігатимете їх auto_ptrsу контейнерах.


39

Дві супер чудові статті на цю тему:


Тому що я думаю, що впродовж майже двох років він, мабуть, мав справу з проблемою, яка порушується.
Щеня

27
@DeadMG: так, ти маєш рацію. Але це не було моєю метою. Якщо хтось колись прийде до цієї теми та захоче дізнатись про неї auto_ptrта інше, ці посилання будуть корисні, я впевнений.
Лазер

Є багато дублікатів, які є новішими.
Щеня

8
@DeadMG: Це запитання не було закрите як дублікат, і тому воно відкрите для продовження. Лазер сказав те, про що раніше не говорили тут. Здається, він прийшов випадково.
Себастьян Мах

Пояснення у другому посиланні, які аналізують проблему після дзвінка sort(), є зрозумілішими за всі відповіді тут.
хаосинк

17

Контейнери STL повинні мати можливість копіювати предмети, які ви зберігаєте в них, і розроблені таким чином, щоб оригінал та копія були рівноцінними. Об'єкти автоматичного вказівника мають зовсім інший контракт, завдяки чому копіювання створює передачу права власності. Це означає, що контейнери auto_ptr виявлятимуть дивну поведінку, залежно від використання.

Існує детальний опис того, що може піти не так, у пункті 8 Effective STL (Скотт Майерс), а також не надто детальний опис у пункті 13 Effective C ++ (Скотт Майерс).


12

У контейнерах STL зберігаються копії вміщених предметів. Коли auto_ptr копіюється, він встановлює старий ptr як нуль. Багато методів контейнера порушуються такою поведінкою.


Але, використовуючи unique_ptr, ви отримуєте майже те саме, оскільки лише один unique_ptr може мати право власності на об'єкт?
Tracer

2
@Tracer, unique_ptrяк і будь-який належний об'єкт C ++ 11, може передавати право власності на свій ресурс лише при побудові переміщення або присвоєнні, гарантуючи, що програміст повинен навмисно передавати std::move(sourceObject)або тимчасове, а не передавати значення l і неінтуїтивно / непередбачувано мутувати його присвоєння копії ... що, як тут ретельно наголошувалося, було основною проблемою auto_ptr.
underscore_d

4

Стандарт C ++ 03 (ISO-IEC 14882-2003) говорить у пункті 20.4.5, параграф 3:

[...] [ Примітка: [...] auto_ptr не відповідає вимогам CopyConstructible та Assignable для елементів контейнера Стандартної бібліотеки, і, отже, створення екземпляра контейнера Стандартної бібліотеки з auto_ptr призводить до невизначеної поведінки. - кінцева примітка ]

Стандарт C ++ 11 (ISO-IEC 14882-2011) у додатку D.10.1, параграф 3, говорить:

[...] Примітка: [...] Екземпляри auto_ptr відповідають вимогам MoveConstructible та MoveAssignable, але не відповідають вимогам CopyConstructible та CopyAssignable. - кінцева примітка]

Стандарт C ++ 14 (ISO-IEC 14882-2014) говорить у додатку C.4.2 Додаток D: особливості сумісності:

Зміна : Шаблони класів auto_ptr, unary_function та binary_function, шаблони функцій random_shuffle та шаблони функцій (та їх типи повернення) ptr_fun, mem_fun, mem_fun_ref, bind1st та bind2nd не визначені.
Обґрунтування : Замінено новими функціями.
Вплив на оригінальну функцію : дійсний код C ++ 2014, який використовує ці шаблони класів та шаблони функцій, може не скомпілювати в цьому міжнародному стандарті.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.