Розумні покажчики: кому належить об’єкт? [зачинено]


114

C ++ - це все про власність на пам'ять - семантика власності .

Це зобов'язання власника шматка динамічно розподіленої пам'яті звільнити цю пам'ять. Тож справді стає питання, кому належить пам’ять.

У C ++ право власності задокументоване типом, який необроблений покажчик загорнутий всередину, таким чином, у хорошій програмі (ІМО) C ++ дуже рідко ( рідко , ніколи ) не бачити перероблених вказівників (так як сировинні покажчики не мають право власності, тому ми можемо не сказати, кому належить пам'ять, і, отже, уважно не читаючи документацію, ви не можете сказати, хто відповідає за право власності).

І навпаки, рідко можна побачити необроблені вказівники, що зберігаються в класі, кожен необроблений покажчик зберігається у власній обгортці інтелектуального вказівника. ( Примітка. Якщо ви не є власником об'єкта, ви не повинні його зберігати, оскільки ви не можете знати, коли він вийде за межі і буде знищений.)

Тож питання:

  • На який тип семантичної форми власності стикаються люди?
  • Які стандартні класи використовуються для реалізації цієї семантики?
  • У яких ситуаціях ви вважаєте їх корисними?

Дозволяє зберігати 1 тип семантичної власності на відповідь, щоб за них можна було проголосувати вгору та вниз окремо.

Підсумок:

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

std::auto_ptr<T>:

Одиничній особі належить об’єкт. Передача права власності дозволена.

Використання: це дозволяє визначати інтерфейси, які показують явну передачу права власності.

boost::scoped_ptr<T>

Одиничній особі належить об’єкт. Передача права власності НЕ дозволена.

Використання: використовується для показу явного права власності. Об'єкт буде знищений деструктором або після явного скидання.

boost::shared_ptr<T>( std::tr1::shared_ptr<T>)

Множинне право власності. Це простий посилальний лічильник покажчика. Коли число відліку досягає нуля, об'єкт руйнується.

Використання: Коли об’єкт може мати декілька власників із строком життя, який неможливо визначити під час компіляції.

boost::weak_ptr<T>:

Використовується shared_ptr<T>в ситуаціях, коли може відбуватися цикл покажчиків.

Використання: використовується для зупинки циклів утримування об'єктів, коли лише цикл підтримує спільну знижку.


14
?? Яке було питання?
Pacerier

9
Я просто хотів зазначити, що оскільки це запитання було розміщено, auto_ptr було застарілим на користь (зараз стандартизованого) unique_ptr
Хуана Кампа

In C++ ownership is documented by the type a RAW pointer is wrapped inside thus in a good (IMO) Чи можна це перефразовувати? Я його зовсім не розумію.
lolololol ol

@lololololol Ви розрізаєте речення навпіл. In C++ ownership is documented by the type a RAW pointer is wrapped inside thus in a good C++ program it is very rare to see RAW pointers passed around. Покажчики RAW не мають семантики власності. Якщо ви не знаєте власника, ви не знаєте, хто несе відповідальність за видалення об'єкта. Існує кілька стандартних класів, які використовуються для загортання покажчиків (std :: shared_ptr, std :: unique_ptr тощо), які визначають право власності, і таким чином визначте, хто відповідає за видалення вказівника.
Мартін Йорк

1
В C ++ 11 + НЕ ВИКОРИСТОВУЙТЕ auto_ptr! Використовуйте натомість унікальний_ptr!
Вал каже: Відновити Моніку

Відповіді:


20

Для мене ці 3 види покривають більшість моїх потреб:

shared_ptr - посилання, що рахується, делокація, коли лічильник досягає нуля

weak_ptr- те саме, що вище, але це "раб" для a shared_ptr, не може розібратися

auto_ptr- коли створення та розселення відбуваються всередині однієї і тієї ж функції, або коли об'єкт повинен вважатися єдиним власником будь-коли. Коли ви присвоюєте один вказівник іншому, другий 'краде' об'єкт у першого.

У мене є власна реалізація для них, але вони також доступні в Boost.

Я все ще передаю об'єкти за посиланням ( constколи це можливо), у цьому випадку викликаний метод повинен вважати, що об'єкт живий лише під час виклику.

Є ще один вид покажчика, який я використовую, що я називаю hub_ptr . Це коли у вас є об'єкт, який повинен бути доступний з об'єктів, вкладених у нього (як правило, як віртуальний базовий клас). Це можна вирішити, передавши weak_ptrїм, але це не має shared_ptrдля себе. Оскільки відомо, що ці об’єкти не живуть довше за нього, він передає їм hub_ptr (це просто обгортка шаблону до звичайного вказівника).


2
Замість того, щоб створити свій власний клас вказівника (hub_ptr), чому б ви просто не передати * це цим об’єктам і дозволити їм зберігати його як орієнтир? Оскільки ви навіть визнаєте, що об'єкти будуть знищені одночасно з класом володіння, я не розумію сенсу стрибати через стільки обручів.
Мішель

4
Це, в основному, договір на дизайн, щоб зробити речі зрозумілими. Коли дочірній об’єкт отримує hub_ptr, він знає, що загострений об’єкт не буде знищений протягом життя дитини і не має на нього права власності. Як об'єкти, що містяться, так і контейнери погоджуються з чітким набором правил. Якщо ви використовуєте голий покажчик, правила можуть бути задокументовані, але компілятор та код не застосовуватимуться.
Фабіо Чеконелло

1
Також зауважте, що ви можете мати #ifdefs, щоб змусити hub_ptr набрати typedef'd до голого вказівника у складах випусків, тому накладні дані існуватимуть лише у збірці налагодження.
Фабіо Чеконелло

3
Зауважте, що документація Boost суперечить вашому опису scoped_ptr. У ньому зазначається, що воно є noncopyableі що право власності не може бути передане.
Алек Томас

3
@ Алек Томас, ти маєш рацію. Я думав про auto_ptr і писав scoped_ptr. Виправлено.
Фабіо Чеконелло

23

Проста модель C ++

У більшості модулів, які я бачив, за замовчуванням передбачалося, що прийом покажчиків не отримує права власності. Насправді функції / методи, що відмовляються від права власності на вказівник, були як дуже рідкісними, так і явно виражали цей факт у своїй документації.

Ця модель передбачає, що користувач є власником лише того, що він / вона прямо виділяє . Все інше автоматично утилізується (при виході за межі або через RAII). Це C-подібна модель, розширена тим фактом, що більшість покажчиків належать об'єктам, які розмещуватимуть їх автоматично або при необхідності (в основному при знищенні зазначених об'єктів) і що тривалість життя об'єктів передбачувана (RAII - ваш друг, знову).

У цій моделі сировинні покажчики вільно циркулюють і здебільшого не небезпечні (але якщо розробник досить розумний, він / вона використовуватиме посилання замість цього, коли це можливо).

  • сирі покажчики
  • std :: auto_ptr
  • boost :: scoped_ptr

Розумна вказівна модель C ++

У коді, повному розумних покажчиків, користувач може сподіватися ігнорувати термін експлуатації об’єктів. Власник ніколи не є кодом користувача: це сам інтелектуальний вказівник (знову RAII). Проблема полягає в тому, що кругові посилання, змішані з підрахунком розумних покажчиків, можуть бути смертельними , тому вам доведеться мати справу як із загальними, так і слабкими вказівниками. Отже, ви все ще маєте враховувати (слабкий покажчик цілком може вказувати ні на що, навіть якщо його перевага перед необробленим покажчиком полягає в тому, що він може вам це сказати).

  • boost :: shared_ptr
  • boost :: слабкий_птр

Висновок

Незалежно від моделей, які я описую, за винятком випадків, коли вказівник не отримує право власності, і все ще дуже важливо знати, хто кому належить . Навіть для C ++ код сильно використовує посилання та / або розумні покажчики.


10

Не має спільного права власності. Якщо це так, переконайтеся, що це лише код, який ви не контролюєте.

Це вирішує 100% проблем, оскільки це змушує вас зрозуміти, як все взаємодіє.


2
  • Спільна власність
  • boost :: shared_ptr

Коли ресурс поділяється між декількома об'єктами. Boost shared_ptr використовує підрахунок посилань, щоб переконатися, що ресурс не виділений, коли всі закінчені.


2

std::tr1::shared_ptr<Blah> найчастіше найкраща ставка.


2
shared_ptr - найпоширеніший. Але є ще багато. У кожного є своя схема використання та хороші та погані місця для подання позову. Трохи більше опису було б непогано.
Мартін Йорк

Якщо ви затрималися зі старшим компілятором, на чому базується boost :: shared_ptr <blah>, на чому базується std :: tr1 :: shared_ptr <blah>. Це досить простий клас, що ви, ймовірно, можете зірвати його з Boost і використовувати його, навіть якщо ваш компілятор не підтримується останньою версією Boost.
Бранан

2

З прискорення є також бібліотека контейнерів вказівників . Вони трохи ефективніші та простіші у використанні, ніж стандартний контейнер розумних покажчиків, якщо ви будете використовувати об’єкти лише в контексті їх контейнера.

У Windows є вказівники COM (IUnknown, IDispatch та друзі) та різні розумні покажчики для їх обробки (наприклад, CComPtr ATL та інтелектуальні покажчики, автоматично створені оператором "import" у Visual Studio на основі класу _com_ptr ).


1
  • Один власник
  • boost :: scoped_ptr

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

Я вважаю це корисним, оскільки його можна легко укласти і випустити, не турбуючись про витік


1

Я не думаю, що я ніколи не мав можливості поділити право власності на свій дизайн. Насправді, з верхньої частини голови я єдиний вірний випадок, про який я можу придуматись, - це модель Flyweight.


1

yasper :: ptr - це легкий, як-от альтернатива boost :: shared_ptr. Це добре працює в моєму (поки що) невеликому проекті.

На веб-сторінці http://yasper.sourceforge.net/ це описано так:

Навіщо писати інший смарт-покажчик C ++? Уже існує кілька високоякісних реалізацій розумних покажчиків для C ++, найбільш чіткий пантеон Boost pointer та Loki's SmartPtr. Для кращого порівняння реалізацій інтелектуальних покажчиків та відповідного їх використання, будь ласка, прочитайте «Нові C ++: Smart (er) покажчики Herb Sutter. На відміну від експансивних особливостей інших бібліотек, Яспер є вузько зосередженим вказівним підрахунком. Це тісно відповідає політиці shared_ptr Boost та політиці Локі RefCount / AllowConversion. Yasper дозволяє програмістам на C ++ забувати про управління пам'яттю, не вводячи великі залежності Boost або не знаючи про складні шаблони політики Локі. Філософія

* small (contained in single header)
* simple (nothing fancy in the code, easy to understand)
* maximum compatibility (drop in replacement for dumb pointers)

Останній пункт може бути небезпечним, оскільки yasper дозволяє ризикувати (в той же час корисними) діями (такими як присвоєння сировинним покажчикам та ручним випуском), заборонених іншими реалізаціями. Будьте уважні, використовуйте ці функції, лише якщо ви знаєте, що робите!


1

Існує ще одна часто використовувана форма одномовного власника, і вона є кращою, auto_ptrоскільки вона дозволяє уникнути проблем, спричинених auto_ptrбожевільною корупцією семантики призначення.

Я кажу ні про кого, окрім як swap. Будь-який тип із відповідною swapфункцією може розглядатися як розумна посилання на якийсь вміст, яким він володіє до тих пір, поки право власності не буде передано іншому тому ж екземпляру шляхом їх заміни. Кожен екземпляр зберігає свою ідентичність, але прив’язується до нового вмісту. Це як безпечно повторювана посилання.

(Це розумний довідник, а не розумний вказівник, тому що вам не доведеться явно розмежувати його, щоб отримати вміст.)

Це означає, що auto_ptr стає менш необхідним - його потрібно лише заповнити прогалини там, де типи не мають хорошої swapфункції. Але всі std контейнери роблять.


Можливо, це стає менш необхідним (я б сказав, що scoped_ptr робить його менш необхідним, ніж це), але це не йде. Функція swap зовсім не допоможе вам, якщо ви виділите щось на купі і хтось кине перед тим, як видалити його, або просто забудете.
Мішель

Саме це я і сказав в останньому абзаці.
Даніель Ервікер

0
  • Один власник: видалити Aka при копіюванні
  • std :: auto_ptr

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

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