розумні покажчики (boost) пояснено


220

Яка різниця між наступним набором покажчиків? Коли ви використовуєте кожен покажчик у виробничому коді, якщо він взагалі є?

Приклади були б вдячні!

  1. scoped_ptr

  2. shared_ptr

  3. weak_ptr

  4. intrusive_ptr

Чи використовуєте ви стимул у виробничому коді?

Відповіді:


339

Основні властивості інтелектуальних покажчиків

Якщо у вас є властивості, ви можете призначити кожному розумному вказівнику. Є три важливі властивості.

  • взагалі немає власності
  • передача права власності
  • частка власності

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

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

Деякі, що володіють розумними покажчиками, не підтримують ні другого, ні третього. Тому їх не можна повернути з функцій або передати десь в іншому місці. Що найбільше підходить для RAIIцілей, коли розумний вказівник зберігається на локальному рівні та просто створюється, тому він звільняє об'єкт після того, як він виходить із сфери застосування.

Частка власності може бути реалізована за допомогою конструктора копій. Це природно копіює інтелектуальний покажчик, і копія, і оригінал посилаються на один і той же об'єкт. Передача права власності реально не може бути реалізована в C ++, оскільки немає засобів для передачі чого-небудь з одного об'єкта на інший, підтримуваний мовою: Якщо ви намагаєтесь повернути об'єкт з функції, то відбувається те, що об'єкт скопіюється. Отже, розумний покажчик, який реалізує передачу права власності, повинен використовувати конструктор копій для здійснення цієї передачі права власності. Однак це, в свою чергу, порушує його використання в контейнерах, оскільки вимоги констатують певну поведінку конструктора копіювання елементів контейнерів, несумісного з цим так званим "рухомим конструктором" поведінкою цих розумних покажчиків.

C ++ 1x забезпечує вбудовану підтримку передачі права власності шляхом введення так званих "конструкторів переміщення" та "операторів присвоєння переміщення". Він також постачається з таким розумним вказівником про передачу власності, який називається unique_ptr.

Класифікація розумних покажчиків

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

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

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

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

unique_ptr- це передача покажчика власності. Ви не можете скопіювати його, але ви можете перемістити його за допомогою конструкторів переміщення C ++ 1x:

unique_ptr<type> p(new type);
unique_ptr<type> q(p); // not legal!
unique_ptr<type> r(move(p)); // legal. p is now empty, but r owns the object
unique_ptr<type> s(function_returning_a_unique_ptr()); // legal!

Це семантичне значення, яке підпорядковує std :: auto_ptr, але через відсутність нашої підтримки для переміщення, він не може забезпечити їх без підводних каменів. unique_ptr автоматично вкраде ресурси з тимчасового іншого унікального_ptr, що є однією з ключових особливостей семантики переміщення. auto_ptr буде застарілим у наступному випуску C ++ Standard на користь унікального_ptr. C ++ 1x також дозволить набивати в контейнери об'єкти, які є лише рухомими, але не підлягають копіюванню. Таким чином, ви можете вкласти, наприклад, унікальні_ptr у вектор. Я зупинюсь тут і посилаюсь на точну статтю про це, якщо ви хочете прочитати більше про це.


3
дякую за похвалу чувак. я ціную це так, що зараз ви також отримаєте +1: p
Йоханнес Шауб - ліб

@litb: я маю сумніви у "передачі права власності"; Я погоджуюсь, що реальної передачі права власності серед об’єктів у C ++ 03 немає, але для розумних покажчиків цього не можна зробити, завдяки деструктивному механізму копіювання, вказаному тут informit.com/articles/article.aspx?p=31529&seqNum= 5 .
legends2k

3
фантастична відповідь. Примітка: auto_ptrвже застаріла (C ++ 11).
nickolay

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

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

91

scoped_ptr - найпростіший. Коли він виходить за межі, він руйнується. Наступний код незаконний (scoped_ptrs не підлягає копіюванню), але він ілюструє точку:

std::vector< scoped_ptr<T> > tPtrVec;
{
     scoped_ptr<T> tPtr(new T());
     tPtrVec.push_back(tPtr);
     // raw T* is freed
}
tPtrVec[0]->DoSomething(); // accessing freed memory

shared_ptr вважається посиланням. Кожен раз, коли відбувається копія або призначення, кількість посилань збільшується. Кожен раз, коли деструктор екземпляра спрацьовує, кількість опорних T * зменшується. Як тільки вона дорівнює 0, вказівник звільняється.

std::vector< shared_ptr<T> > tPtrVec;
{
     shared_ptr<T> tPtr(new T());
     // This copy to tPtrVec.push_back and ultimately to the vector storage
     // causes the reference count to go from 1->2
     tPtrVec.push_back(tPtr);
     // num references to T goes from 2->1 on the destruction of tPtr
}
tPtrVec[0]->DoSomething(); // raw T* still exists, so this is safe

weak_ptr слабка посилання на загальний покажчик , який вимагає від вас , щоб перевірити, якщо загострений до shared_ptr ще навколо

std::vector< weak_ptr<T> > tPtrVec;
{
     shared_ptr<T> tPtr(new T());
     tPtrVec.push_back(tPtr);
     // num references to T goes from 1->0
}
shared_ptr<T> tPtrAccessed =  tPtrVec[0].lock();
if (tPtrAccessed[0].get() == 0)
{
     cout << "Raw T* was freed, can't access it"
}
else
{
     tPtrVec[0]->DoSomething(); // raw 
}

intrusive_ptr зазвичай використовується, коли є сторонній інтелектуальний ptr, який ви повинні використовувати. Він запустить безкоштовну функцію для додавання та зменшення кількості посилань. Перегляньте посилання для збільшення документації для отримання додаткової інформації.


хіба не if (tPtrAccessed[0].get() == 0)припустимо бути if (tPtrAccessed.get() == 0) ?
Раджешвар

@DougT. Чи вірите ви, що Java використовує ту саму ідею з References? М'які, жорсткі, слабкі тощо?
gansub

20

Не забувайте boost::ptr_containerв жодному опитуванні підвищення розумних покажчиків. Вони можуть бути безцінними в ситуаціях, коли напр. std::vector<boost::shared_ptr<T> >, Буде занадто повільно.


Насправді, востаннє, коли я спробував це, тестування показало, що розрив у продуктивності значно зменшився, оскільки я спочатку писав це, принаймні, на типовому ПК HW! Більш ефективний підхід ptr_container все ж може мати певні переваги у випадках використання ніші.
тайм

12

Другу пораду щодо перегляду документації. Це не так страшно, як здається. І кілька коротких підказок:

  • scoped_ptr- покажчик автоматично видаляється, коли він виходить за межі області. Примітка - призначення не можливе, але не вносить накладних витрат
  • intrusive_ptr- посилання підрахунку посилань без накладних витрат smart_ptr. Однак сам об'єкт зберігає довідкову кількість
  • weak_ptr- працює разом із shared_ptrвирішенням ситуацій, що призводять до кругових залежностей (читайте документацію та шукайте в Google для отримання приємної картини;)
  • shared_ptr - загальні, найпотужніші (і важкі) розумних покажчиків (з тих, які пропонує boost)
  • Існує також старе auto_ptr, що забезпечує об'єкт, на який він вказує, автоматично знищується, коли контроль залишає область. Однак він відрізняється семантикою копіювання, ніж решта хлопців.
  • unique_ptr- поставляється із C ++ 0x

Відповідь на редагування: Так


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