Які реалізовані програми C ++ Smart Pointer доступні?


121

Порівняння, плюси, мінуси та коли їх використовувати?

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

Зрештою, питання полягає в тому, які існують інтелектуальні покажчики в C ++ там і як вони порівнюються? Лише прості плюси і мінуси або винятки і припущення для чогось, на що ви, напевно, можете вважати, має спрацювати.

Я опублікував декілька реалізацій, які я використав або принаймні зафіксував і розглядаю як відповідь нижче, і моє розуміння їх відмінностей та подібностей, які можуть бути не на 100% точними, тому сміливо перевіряйте чи виправляйте мене за необхідності.

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


5
Я думаю, що це слід повторно розмістити як відповідь на це питання, а питання перетворити на актуальне питання. Інакше я відчуваю, що люди закриють це як "не справжнє питання".
страгер

3
Є всілякі інші розумні покажчики, наприклад, смарт-покажчики ATL або OpenSceneGraphosg::ref_ptr .
Джеймс Мак-Нілліс

11
Тут є питання?
Коді Грей

6
Я думаю, що ви неправильно зрозуміли std::auto_ptr. std::auto_ptr_refє деталізацією дизайну std::auto_ptr. std::auto_ptrне має нічого спільного зі збиранням сміття, його головна мета полягає в тому, щоб виключно безпечна передача права власності, особливо у ситуаціях виклику функцій та повернення функцій. std::unique_ptrможе вирішити лише "проблеми", які ви цитуєте зі стандартними контейнерами, оскільки C ++ змінився, щоб дозволити розмежування переміщення та копіювання, а стандартні контейнери змінилися, щоб скористатися цим.
CB Bailey

3
Ви кажете, що ви не фахівець з розумних покажчиків, але резюме є досить вичерпним та правильним (за винятком незначних підказок щодо auto_ptr_refдеталізації реалізації). Але я погоджуюся, що ви повинні розмістити це як відповідь і переформулювати це питання як актуальне питання. Потім це може слугувати подальшим орієнтиром.
Конрад Рудольф

Відповіді:


231

C ++ 03

std::auto_ptr- Можливо, один із оригіналів, яким він страждав від синдрому першої тяги, лише надав обмежені можливості для збору сміття. Першим недоліком є ​​те, що він вимагає deleteруйнування, що робить їх неприйнятними для зберігання виділених масивів ( new[]). Він займає право власності на вказівник, тому два автоматичні вказівники не повинні містити одного об’єкта. Призначення передасть право власності та скине автоматичний покажчик rvalue на нульовий покажчик. Що призводить до, можливо, найгіршого недоліку; їх не можна використовувати в контейнерах STL через вищезазначену неможливість копіювання. Останнім ударом для будь-якого випадку використання є те, що їх планування буде припинено в наступному стандарті C ++.

std::auto_ptr_ref- Це не розумний покажчик, це фактично деталі дизайну, які використовуються разом, std::auto_ptrщоб дозволити копіювання та призначення у певних ситуаціях. Зокрема, його можна використовувати для перетворення non-const std::auto_ptrна значення, використовуючи трюк Colvin -Gibbons, також відомий як конструктор переміщення для передачі права власності.

Навпаки, можливо, std::auto_ptrнасправді не передбачалося використовувати розумний покажчик загального призначення для автоматичного вивезення сміття. Більшість моїх обмежених розумінь та припущень ґрунтуються на ефективному використанні auto_ptr Herb Sutter, і я користуюсь ним регулярно, хоча не завжди найбільш оптимізованим способом.


C ++ 11

std::unique_ptr- Це наш друг, який замінить std::auto_ptrйого, буде досить схожим, за винятком ключових вдосконалень, щоб виправити слабкі місця, std::auto_ptrяк, наприклад, робота з масивами, захист lvalue через приватний конструктор копій, використання з контейнерами та алгоритмами STL тощо. і слід пам’яті обмежений, це ідеальний кандидат для заміни або, можливо, більш влучно описаного як володіння, необробленими покажчиками. Як випливає з "унікального", існує лише один власник вказівника, як і попередній std::auto_ptr.

std::shared_ptr- Я вважаю, що це базується на TR1, boost::shared_ptrале вдосконалено, щоб включити також арифметику збитку та покажчика. Коротше кажучи, він обертає посилальний підрахунок розумного вказівника навколо динамічно виділеного об'єкта. Оскільки "загальний" означає, що вказівник може бути власником більш ніж одного спільного покажчика, коли остання посилання останнього спільного вказівника виходить за межі, тоді об'єкт буде видалено відповідним чином. Вони також є безпечними для потоків і в більшості випадків можуть обробляти неповні типи. std::make_sharedможе бути використана для ефективної побудови std::shared_ptrасигнування за допомогою одного розподільника за замовчуванням.

std::weak_ptr- Аналогічно спираються на TR1 та boost::weak_ptr. Це посилання на об'єкт, що належить a, std::shared_ptrі тому не перешкоджатиме видаленню об'єкта, якщо кількість std::shared_ptrпосилань опуститься до нуля. Щоб отримати доступ до необробленого покажчика, спочатку вам потрібно буде отримати доступ до std::shared_ptrвиклику, lockякий поверне порожній, std::shared_ptrякщо термін дії власного вказівника закінчився та вже знищений. Це в першу чергу корисно, щоб уникнути невизначеного підрахунку опорних підрахунків при використанні декількох інтелектуальних покажчиків.


Підвищення

boost::shared_ptr- Мабуть, найпростіший у використанні для найрізноманітніших сценаріїв (STL, PIMPL, RAII тощо). Це спільний посилання, що рахується розумним покажчиком. Я чув декілька скарг на продуктивність та накладні витрати в деяких ситуаціях, але, мабуть, проігнорував їх, бо не можу пригадати, в чому був аргумент. Мабуть, це було досить популярно, щоб стати типовим об'єктом C ++, і жодних недоліків щодо норми щодо розумних покажчиків не виникає на увазі.

boost::weak_ptr- Так само, як і попередній опис std::weak_ptr, на основі цієї реалізації, це дозволяє не володіти посиланням на a boost::shared_ptr. Ви не дивно закликаєте lock()отримати доступ до "сильного" спільного вказівника і маєте перевірити, чи він дійсний, оскільки він міг уже бути знищений. Просто переконайтеся, що не зберігайте повернутий спільний покажчик, і нехай він виходить із сфери дії, як тільки ви закінчите з ним, інакше ви знову повернетесь до циклічної посилальної проблеми, коли ваші посилання будуть висіти, а об'єкти не будуть знищені.

boost::scoped_ptr- Це простий клас розумних покажчиків з невеликими накладними витратами, ймовірно, розроблений для кращої альтернативи, boost::shared_ptrколи можна використовувати. Це порівняно std::auto_ptrособливо з тим, що він не може бути безпечно використаний як елемент контейнера STL або з кількома вказівниками на один і той же об'єкт.

boost::intrusive_ptr- Я ніколи цього не використовував, але, наскільки я розумію, це розроблено для використання під час створення власних класів, сумісних із смарт-покажчиком. Вам потрібно реалізувати підрахунок посилань самостійно, вам також потрібно буде застосувати кілька методів, якщо ви хочете, щоб ваш клас був загальним, крім того, вам доведеться реалізувати власну безпеку потоку. З іншого боку, це, ймовірно, дає вам самий нестандартний спосіб вибору та вибору саме того, скільки або як мало "розумності" ви хочете. intrusive_ptrяк правило, більш ефективна, ніж shared_ptrоскільки дозволяє отримати єдине розподілення купи на один об'єкт. (спасибі Арвід)

boost::shared_array- Це boost::shared_ptrдля масивів. В основному new [], operator[]і, звичайно delete [], запікаються. Це можна використовувати в контейнерах STL і, наскільки я знаю, все boost:shared_ptrробить, хоча ви не можете використовувати boost::weak_ptrїх. Однак ви можете використати boost::shared_ptr<std::vector<>>подібний функціонал і відновити можливість використання boost::weak_ptrпосилань.

boost::scoped_array- Це boost::scoped_ptrдля масивів. Як і у boost::shared_arrayвсіх необхідних масивах, які входять до складу масиву, випікається. Цей не можна скопіювати, тому його не можна використовувати в контейнерах STL. Я майже де завгодно знайшов бажання скористатися цим, напевно, ви могли просто скористатися std::vector. Я ніколи не визначав, що насправді швидше або має менші накладні витрати, але цей масштабний масив здається значно менш задіяним, ніж вектор STL. Коли ви хочете зберегти розподіл у стеці, розгляньте boost::arrayзамість цього.


Qt

QPointer- Введений у Qt 4.0, це "слабкий" розумний покажчик, який працює лише з QObjectкласами, які є похідними, які в рамках Qt - це майже все, тому це насправді не обмеження. Однак є обмеження, а саме те, що він не надає "сильний" покажчик, і хоча ви можете перевірити, чи лежить в основі об'єкт, isNull()ви можете виявити, що ваш об'єкт знищений відразу після того, як ви пройдете цю перевірку, особливо в багатопотокових середовищах. Qt люди вважають це застарілим, я вважаю.

QSharedDataPointer- Це "сильний" розумний вказівник, який потенційно можна порівняти з тим, що boost::intrusive_ptrвін має вбудовану безпеку потоку, але він вимагає включення методів підрахунку посилань ( refі deref), які ви можете зробити шляхом підкласифікації QSharedData. Як і у більшості Qt, об'єкти найкраще використовувати через достатнє успадкування та підкласифікацію.

QExplicitlySharedDataPointer- Дуже схожий, за QSharedDataPointerвинятком того, що це неявно не викликає detach(). Я б назвав цю версію 2.0 QSharedDataPointerтакою, як незначне посилення контролю щодо того, коли саме потрібно відключити після того, як число відліку зменшиться до нуля, не особливо вартує абсолютно нового об’єкта.

QSharedPointer- Атомний підрахунок посилань, безпечний потік, роздільний покажчик, користувацькі видалення (підтримка масиву), схоже на все, що має бути розумним вказівником. Це те, що я в першу чергу використовую як розумний покажчик у Qt, і я вважаю його порівнянним, boost:shared_ptrхоча, ймовірно, значно більше накладних, як і багато об'єктів у Qt.

QWeakPointer- Ви відчуваєте повторюваний зразок? Так само, як std::weak_ptrі boost::weak_ptrце використовується в поєднанні з тим, QSharedPointerколи вам потрібні посилання між двома розумними покажчиками, які в іншому випадку призведе до того, що ваші об'єкти ніколи не будуть видалені.

QScopedPointer- Ця назва також повинна виглядати знайомою і насправді заснована на boost::scoped_ptrвідміну від версій Qt спільних і слабких покажчиків. Він функціонує, щоб забезпечити розумний вказівник для одного власника, без накладних витрат, QSharedPointerякий робить його більш придатним для сумісності, безпечного коду для виключення та всіх речей, які ви можете використовувати std::auto_ptrабо boost::scoped_ptrдля використання .


1
дві речі, які, на мою думку, заслуговують на згадку: intrusive_ptrяк правило, більш ефективні, ніж shared_ptrвони дозволяють мати один розподіл купи на один об'єкт. shared_ptrв загальному випадку виділить окремий невеликий об’єкт купи для опорних лічильників. std::make_sharedможе бути використаний, щоб отримати найкраще з обох світів. shared_ptrз єдиним виділенням купи.
Арвід

У мене, можливо, не пов'язане питання: чи можна здійснити збір сміття, просто замінивши всі покажчики на shared_ptrs? (Не рахуючи вирішення циклічних посилань)
Seth Carnegie

@Seth Carnegie: Не всі покажчики збираються вказувати на щось, виділене у вільному магазині.
Силіко

2
@the_mandrill Але це працює, якщо деструктор класу володіння визначений в окремому блоці перекладу (.cpp-файл), ніж код клієнта, який у Pimpl-ідіомі надається все одно. Оскільки цей блок перекладу зазвичай знає повне визначення Pimpl і тому його деструктор (коли він знищує auto_ptr) правильно знищує Pimpl. Я також мав побоювання з цього приводу, коли побачив ці попередження, але я спробував це, і воно спрацьовує (деструктор Pimpl називається). PS .: будь ласка, використовуйте для мене @ -syntax, щоб побачити відповіді.
Крістіан Рау

2
Корисність списку була збільшена шляхом додавання відповідних посилань на документи.
улідко

5

Є також Локі, який реалізує інтелектуальні покажчики на основі політики.

Інші посилання на інтелектуальні покажчики, засновані на політиці, вирішують проблему поганої підтримки оптимізації порожньої бази та багаторазового успадкування багатьма компіляторами:


1

Окрім наведених, є і деякі, орієнтовані на безпеку:

SaferCPlusPlus

mse::TRefCountingPointerце посилання на підрахунок розумного вказівника, як std::shared_ptr. Різниця полягає в тому, що mse::TRefCountingPointerвона безпечніша, менша та швидша, але не має жодного механізму захисту різьби. І він поставляється у версіях "не нульовий" та "фіксований" (неповторний), за які можна сміливо вважати, що завжди вказують на дійсно виділений об'єкт. Отже, якщо ваш цільовий об'єкт розділений між асинхронними потоками, тоді використовуйте std::shared_ptr, інакше mse::TRefCountingPointerце більш оптимально.

mse::TScopeOwnerPointerсхожий на boost::scoped_ptr, але працює в поєднанні з mse::TScopeFixedPointer"сильним-слабким" вказівником відносин типу, як std::shared_ptrі std::weak_ptr.

mse::TScopeFixedPointerвказує на об’єкти, які виділяються на стеку, чий "володіє" покажчик виділяється на стеку. Він (навмисно) обмежений у своїй функціональності, щоб підвищити безпеку часу компіляції без витрат на виконання. Суть покажчиків «сфери застосування» полягає в тому, щоб визначити сукупність обставин, які досить прості та детерміновані, що не потрібні механізми безпеки (під час виконання).

mse::TRegisteredPointerповодиться як необроблений покажчик, за винятком того, що його значення автоматично встановлюється на null_ptr при знищенні цільового об'єкта. Він може використовуватися як загальна заміна сировинних покажчиків у більшості ситуацій. Як і сирий вказівник, він не має внутрішньої безпеки потоку. Але в обмін це не має жодних проблем з націленням об'єктів, виділених на стеку (та отримання відповідних переваг продуктивності). Якщо включені перевірки часу запуску, цей вказівник захищений від доступу до недійсної пам'яті. Оскільки mse::TRegisteredPointerмає таку саму поведінку, як і необроблений покажчик при вказівці на дійсні об'єкти, його можна "відключити" (автоматично замінити відповідним необробленим вказівником) за допомогою директиви про час компіляції, що дозволяє використовувати його для допомоги в пошуку помилок у налагодженні / тесті / бета-режими, не вимагаючи накладних витрат у режимі релізу.

Ось стаття, що описує, чому і як ними користуватися. (Зверніть увагу, безсоромна пробка.)

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