std :: shared_ptr в крайньому випадку?


59

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

Хтось би потурбував би пояснити це дещо глибше? Як ми можемо програмувати без std::shared_ptrі до сих пір управляти об'єктні життєвим раз в безпечному шляху?


8
Не використовуєте покажчики? Маючи чіткого власника об'єкта, який управляє протягом усього життя?
Бо Персон

2
як щодо явно наданих даних? Важко не використовувати покажчики. Також std :: shared_pointer зробить брудним "керувати життям" у цьому випадку
Каміль Климек

6
Чи розглядали ви менше слухати подані поради та більше аргументувати цю пораду? Він досить добре пояснює, в якій системі працювали б такі поради.
Ніколь Болас

@NicolBolas: Я прислухався до порад та аргументів, але, очевидно, не відчував, що зрозумів це досить добре.
ronag

У який час він каже "в крайньому випадку"? Дивлячись біт на 36 хвилині в ( channel9.msdn.com/Events/GoingNative/GoingNative-2012/… ), він каже, що він насторожено ставиться до використання покажчиків, але він має на увазі загальні вказівники, а не просто shared_ptr та unique_ptr, але навіть ' звичайний 'покажчик. Він означає, що слід віддавати перевагу самим об'єктам (а не вказівникам на об’єкти, виділені новим). Чи був шматочок, про який ви думали пізніше у презентації?
Фарап

Відповіді:


55

Якщо ви можете уникнути спільної власності, тоді ваш додаток буде простішим і простішим для розуміння, а отже, менш сприйнятливим до помилок, що вводяться під час обслуговування. Складні або незрозумілі моделі власності, як правило, призводять до того, що складно слідкувати за зв'язками різних частин програми через загальний стан, який може бути непросто відстежувати.

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


5
+1 за те, що заявляє, що проблема полягає не в самій техно (спільне володіння), а в труднощах, які вона створює для нас, просто людей, яким потім доведеться розшифровувати те, що відбувається.
Матьє М.

Однак використання такого підходу суттєво обмежить можливість програміста застосовувати шаблони програмування одночасності для більшості нетривіальних класів OOP (через не копіюваність.) Ця проблема порушена в "Going Native 2013".
rwong

48

Світ, в якому живе Б'ярн, дуже ... академічний, заради кращого терміну. Якщо ваш код може бути розроблений і структурований таким чином, що об’єкти мають дуже навмисні реляційні ієрархії, такі, що відносини власності є жорсткими і непорушними, код перетікає в одну сторону (від високого до низького рівня), а об'єкти розмовляють лише з тими, хто нижче ієрархія, тоді ви не знайдете великої потреби shared_ptr. Це те, що ви використовуєте в тих рідкісних випадках, коли хтось повинен порушувати правила. Але в іншому випадку ви можете просто вставити все в vectors або інші структури даних, що використовують значення семантики, і unique_ptrs для речей, які вам потрібно виділити поодинці.

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

У такій системі проведення голих покажчиків - це не небезпечно точно, але це викликає питання. Найкраще в shared_ptrтому, що він забезпечує розумні синтаксичні гарантії щодо терміну експлуатації об'єкта. Чи можна її зламати? Звичайно. Але люди також можуть const_castщось робити; Основний догляд та годування shared_ptrповинні забезпечувати розумну якість життя виділених об’єктів, права власності яких повинні бути спільними.

Потім є weak_ptrs, які неможливо використати за відсутності a shared_ptr. Якщо ваша система жорстко структурована, ви можете зберігати голий вказівник на якийсь об’єкт, впевнений у тому, що структура програми гарантує, що об’єкт, на який вказували, пережив вас. Ви можете викликати функцію, яка повертає вказівник на якесь внутрішнє або зовнішнє значення (наприклад, знайдіть об'єкт з назвою X). У правильно структурованому коді ця функція буде доступна вам лише у тому випадку, якщо тривалість життя об'єкта гарантовано перевищить вашу власну; таким чином, зберігати цей голий вказівник у вашому об’єкті добре.

Оскільки цієї жорсткості не завжди вдається досягти в реальних системах, вам потрібен певний спосіб розумного забезпечення терміну експлуатації. Іноді вам не потрібно повного права власності; іноді просто потрібно вміти знати, коли вказівник поганий чи хороший. Ось де це weak_ptrвходить. Були випадки, коли я міг би використовувати unique_ptrабо boost::scoped_ptr, але мені довелося скористатись тим, shared_ptrщо мені спеціально потрібно було дати комусь "мінливий" покажчик. Вказівник, який термін життя був невизначений, і вони могли запитувати, коли цей покажчик був знищений.

Безпечний спосіб вижити, коли стан світу невизначений.

Могло це зробити якийсь виклик функції, щоб отримати вказівник, а не через weak_ptr? Так, але це можна було легше зламати. Функція, яка повертає голий покажчик, не може синтаксично запропонувати користувачеві робити щось на зразок зберігати цей покажчик довгостроково. Повернення shared_ptrтакож робить його занадто простим для того, щоб хтось просто зберігав його і потенційно продовжував тривалість життя предмета. weak_ptrОднак повернення настійно говорить про те, що зберігання отриманого shared_ptrвами lock- це ... сумнівна ідея. Це не завадить вам зробити це, але нічого в C ++ не заважає вам порушити код. weak_ptrзабезпечує деякий мінімальний опір від природної речі.

Тепер це не означає, що shared_ptrне можна перестаратися ; це, звичайно, може. Особливо до- unique_ptr, було багато випадків, коли я просто використовував, boost::shared_ptrтому що мені потрібно було передати вказівник RAII навколо або внести його до списку. Без рухомої семантики і unique_ptr, boost::shared_ptrбуло єдиним реальним рішенням.

І використовувати його можна в місцях, де це зовсім непотрібно. Як було сказано вище, правильна структура коду може усунути потребу в деяких використання shared_ptr. Але якщо ваша система не може бути структурована як така і все-таки робити все, що їй потрібно, shared_ptrбуде корисною.


4
+1: Подивіться, наприклад, на boost :: asio. Я думаю, що ця ідея поширюється на багато областей, ви, можливо, не знаєте під час компіляції, який віджет UI або асинхронний виклик є останнім, щоб відмовитись від об'єкта, а з shared_ptr вам не потрібно знати. Очевидно, це не стосується кожної ситуації, а лише інший (дуже корисний) інструмент у вікні інструментів.
Гай Сіртон

3
Трохи запізнілий коментар; shared_ptrчудово підходить для систем, де c ++ інтегрований із мовою скриптів, наприклад, python. Використовуючи boost::pythonпідрахунок посилань на стороні c ++ та python, сильно співпрацює; будь-який об’єкт із c ++ все ще може зберігатися в python і він не загине.
eudoxos

1
Для довідки моє розуміння - це ні використання WebKit, ні Chromium shared_ptr. Обидва використовують власні реалізації intrusive_ptr. Я
доводжу

1
@gman: Я вважаю ваш коментар дуже оманливим, оскільки заперечення Stroustrup shared_ptrстосується однаково intrusive_ptr: він заперечує проти всієї концепції спільної власності, а не до будь-якого конкретного написання концепції. Тому для цього питання це два реальні приклади великих додатків, які справді використовуються shared_ptr. (І ще більше, вони демонструють, що shared_ptrкорисно навіть тоді, коли це не дозволяє weak_ptr.)
ruakh

1
FWIW, щоб протистояти твердженню, що Bjarne живе в академічному світі: за всю мою суто індустріальну кар'єру (яка включала співавторство на фондовій біржі G20 та виключно архітектуру MOG-плеєра 500K), я бачив лише 3 випадки, коли нам насправді потрібно було спільна власність. Я на 200% з Bjarne тут.
Заєць без клопів

37

Я не вірю, що я коли-небудь використовував std::shared_ptr.

Велику частину часу об’єкт асоціюється з якоюсь колекцією, до якої він належить протягом усього життя. У такому випадку ви можете просто використовувати whatever_collection<o_type>або whatever_collection<std::unique_ptr<o_type>>, що колекція є членом об'єкта або автоматичної змінної. Звичайно, якщо вам не потрібна динамічна кількість об'єктів, ви можете просто використовувати автоматичний масив фіксованого розміру.

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


Ніколь Болас зауважив, що "Якщо якийсь об'єкт тримається за голий вказівник і цей об'єкт гине ... ой" і "Об'єкти повинні гарантувати, що об'єкт проживає протягом життя цього об'єкта. Тільки це shared_ptrможна зробити".

Я не купую цей аргумент. Принаймні, це не shared_ptrвирішує цю проблему. Що стосовно:

  • Якщо якась хеш-таблиця міститься на об'єкті, а хеш-код цього об’єкта змінюється ... ой.
  • Якщо якась функція є ітерацією вектора, і елемент вставляється в цей вектор ... ой.

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

Об'єкти не «гинуть», якийсь фрагмент коду їх знищує. І кидати shared_ptrпроблему замість того, щоб розібратися з контрактом на виклик, - помилкова безпека.


17
@ronag: Я підозрюю, що ви почали використовувати його там, де сирий покажчик був би кращим, тому що "сировинні покажчики погані". Але сировинні покажчики непогані . Лише створення першого, володіння вказівником на об’єкт необробленим покажчиком є ​​поганим, оскільки тоді вам доведеться керувати пам'яттю вручну, що за наявності винятків нетривіально. Але використовувати необроблені покажчики в якості ручок або ітераторів просто чудово.
Бен Войгт

4
@BenVoigt: Звичайно, складність у переході навколо голих покажчиків полягає в тому, що ти не знаєш тривалість життя об’єктів. Якщо якийсь об’єкт тримається за голий вказівник і цей об’єкт вмирає ... ой. Саме такі речі shared_ptrі weak_ptrбули покликані уникати. Bjarne намагається жити у світі, якщо все було приємне, чітке життя, і все побудовано навколо цього. І якщо ти можеш побудувати цей світ, чудово. Але це не так, як це в реальному світі. Об'єкти повинні гарантувати, що об'єкт живе протягом життя цього об'єкта. Тільки це shared_ptrможна зробити.
Ніколь Болас

5
@NicolBolas: Це помилкова безпека. Якщо абонент функції не надає звичайної гарантії: "Цей об'єкт не буде торкатися жодної сторони під час виклику функції", тоді обом потрібно узгодити, який тип зовнішніх модифікацій дозволений. shared_ptrлише пом'якшує одну конкретну зовнішню модифікацію, навіть не найпоширенішу. І відповідальність за забезпечення життєздатності об'єкта не є відповідальною, якщо в договорі виклику функції визначено інше.
Бен Войгт

6
@NicolBolas: Якщо функція створює об'єкт і повертає його вказівником, він повинен бути a unique_ptr, виражаючи, що існує лише один вказівник на об'єкт і він має право власності.
Ben Voigt

6
@Nicol: Якщо він шукає вказівник у якійсь колекції, він, ймовірно, повинен використовувати будь-який тип вказівника у цій колекції, або нераціональний покажчик, якщо колекція містить значення. Якщо він створює об'єкт і абонент хоче a shared_ptr, він все одно повинен повернути a unique_ptr. Перетворення від unique_ptrдо shared_ptrлегко, але зворотно логічно неможливо.
Ben Voigt

16

Я вважаю за краще не думати в абсолютному виразі (наприклад, "в крайньому випадку"), а щодо проблемної області.

C ++ може запропонувати декілька різних способів управління життям. Деякі з них намагаються повторно проводити об'єкти керованим стеком. Деякі інші намагаються уникнути цього обмеження. Деякі з них є "буквальними", інші - наближеннями.

Насправді ви можете:

  1. використовувати семантику чистого значення . Працює для відносно невеликих об'єктів, де важливими є "цінності", а не "тотожність", де можна припустити, що два, що Personмають однакову, nameє однією і тією ж людиною (краще: два представлення однієї і тієї ж людини ). Термін експлуатації надається машинним стеком, в кінці - по суті, не має значення для програми (оскільки людина це звали , незалежно від того Person, що він несе)
  2. використовувати стек виділених об'єктів та пов'язані з ним посилання або покажчики: дозволяє поліморфізм та надає термін експлуатації об’єкту. Не потрібно "розумних покажчиків", оскільки ви гарантуєте, що жоден об'єкт не може бути "вказуваний" структурами, які залишають у стеці довше об'єкта, на який вони вказують (спочатку створіть об'єкт, потім структури, які посилаються на нього).
  3. використовуйте об'єкти, що управляються купою виділених об'єктів : це те, що робить std :: vector і всі контейнери, і wat std::unique_ptr(це можна вважати вектором з розміром 1). Знову ж таки, ви визнаєте, що об'єкт починає існувати (і закінчувати своє існування) до (після) структури даних, на яку вони посилаються.

Слабкою стороною цього мехтоду є те, що типи та величини об'єктів не можуть змінюватись під час виконання більш глибоких викликів рівня стека стосовно місця їх створення. Усі ці прийоми "не вживають" своєї сили у всій ситуації, коли створення та видалення об'єкта є наслідком діяльності користувача, так що тип виконання об'єкта не є відомим у компільований час і може виникнути надструктура, що посилається на об'єкти Користувач просить видалити з більш глибокого виклику функції рівня стека. У цьому випадку вам потрібно:

  • ввести деяку дисципліну щодо управління об'єктом та пов'язаними з ним референтними структурами або ...
  • перейти якось до темної сторони "виходу з чистого стека на основі життя": об'єкт повинен залишити незалежно від функцій, які їх створили. І повинні піти ... поки вони не знадобляться .

У C ++ isteslf немає жодного вбудованого механізму для моніторингу цієї події ( while(are_they_needed)), отже, ви повинні наблизитись до:

  1. використовувати спільну власність : життя об'єктів пов'язане з "контрольним лічильником": працює, якщо "право власності" може бути ієрархічно організовано, не вдається, коли можуть існувати петлі власності. Це те, що робить std :: shared_ptr. І слабкий_птр може бути використаний для розбиття циклу. Це працює більшу частину часу, але не вдається у великому дизайні, де багато дизайнерів працюють в різних командах, і немає чіткої причини (щось випливає з певної вимоги) про те, хто повинен мати що (типовим прикладом є подвійні ланцюги: це попереднє внаслідок наступного посилання на попереднє або наступне володіння попереднім посиланням на наступне? Якщо немає вимоги, то рішення рівнозначні, і у великому проекті ви ризикуєте їх змішати)
  2. Використовуйте групу для збору сміття : Вам просто не важливо все життя. Ви час від часу запускаєте колектор, і те, що недосяжно, вважається "вже не потрібним" і ... ну ... гм ... знищено? завершено? заморожений ?. Є декілька колекторів GC, але я ніколи не знаходжу такого, який справді знає C ++. Більшість з них вільно пам'ять, не піклуючись про знищення об'єктів.
  3. Використовуйте сміттєзбірник C ++ з належним стандартним інтерфейсом. Успіхів знайти його.

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

Сміттєзбірник коштує, shared_ptr менше, унікальний_ptr ще менше, а керованих стеками об’єктів дуже мало.

Це shared_ptr"остання інстанція" ?. Ні, це не так: остання інстанція - сміттєзбірники. shared_ptrнасправді std::запропонований крайній захід. Але може бути правильним рішенням, якщо ви опинилися в ситуації, яку я пояснив.


9

Єдине, що згадує Герб Саттер на наступній сесії, - це те, що кожного разу, коли ви копіюєте, shared_ptr<>виникає замкнутий приріст / декремент, який має відбутися. Для багатопотокового коду в багатоядерній системі синхронізація пам'яті не є незначною. Враховуючи вибір, краще використовувати або значення стека, або a unique_ptr<>і проходити навколо посилань або необроблених покажчиків.


1
Або пройти shared_ptrповз lvalue або rvalue посилання ...
ronag

8
Справа в тому, що не просто використовуйте так, shared_ptrяк саме срібна куля вирішить усі ваші проблеми з витоком пам’яті лише тому, що це в стандарті. Це заманлива пастка, але все ж важливо бути обізнаним про право власності на ресурси, і якщо ця власність не поділяється, a shared_ptr<>- це не найкращий варіант.
Затемнення

Для мене це найменш важлива деталь. Див. Передчасну оптимізацію. У більшості випадків це не повинно керувати рішенням.
Гай Сіртон

1
@gbjbaanb: так, вони на рівні процесора, але в багатоядерній системі ви недійсні кеші та примушуєте бар'єри пам'яті.
Eclipse

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

7

Я не пам'ятаю, чи був останнім "курорт" саме те слово, яке він використав, але я вважаю, що власне значення того, що він сказав, був останнім "вибором": з урахуванням чітких умов власності; унікальні_ptr, слабкі_птр, спільні_птр і навіть голі покажчики мають своє місце.

Єдине, про що вони погодилися, - це те, що ми (розробники, автори книг тощо) усі на «фазі навчання» C ++ 11, і визначаються закономірності та стилі.

Як приклад, Герб пояснив, що слід очікувати нових випусків деяких початкових книг C ++, таких як Ефективні стандарти C ++ (Мейєрс) та Стандарти кодування C ++ (Sutter & Alexandrescu), через кілька років, в той час як досвід та кращі практики роботи з C ++ 11 каструль.


5

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

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

Об'єкт A, містить загальний покажчик на інший Об'єкт A Об'єкт B створює A a1 і A a2 і призначає a1.otherA = a2; і а2.іншийA = a1; Тепер загальні вказівники об'єкта B, які він використовував для створення a1, a2, виходять із сфери застосування (скажімо, наприкінці функції). Тепер у вас є витік - ніхто більше не посилається на a1 і a2, але вони посилаються один на одного, тому їх кількість посилань завжди 1, і ви просочилися.

Ось простий приклад, коли це відбувається в реальному коді, це відбувається зазвичай складними способами. Є рішення з слабким_птром, але так багато людей просто просто діляться_птр скрізь і навіть не знають про проблему витоку або навіть про слабкий_птр.

Щоб завершити це: я думаю, що коментарі, на які посилається ОП, зводяться до цього:

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

редагувати: навіть якщо це означає "невідомо, мені потрібно використовувати shared_ptr", ви все ще думали про це і робите це навмисно.


3

Я відповім із досвіду роботи з Objective-C, мовою, де всі об'єкти посилаються на облік і розподіляються на купі. Через те, що є один спосіб поводження з об'єктами, програмісту набагато простіше. Це дозволило визначити стандартні правила, які при дотриманні гарантують надійність коду і не протікають пам'яті. Це також дозволило створити розумні оптимізації компілятора, як останній ARC (автоматичний підрахунок посилань).

Моя думка, що shared_ptr повинен бути вашим першим варіантом, а не останнім засобом. Використовуйте підрахунок посилань за замовчуванням та інші параметри, лише якщо ви впевнені в тому, що робите. Ви будете більш продуктивними, і ваш код буде більш надійним.


1

Я спробую відповісти на питання:

Як ми можемо програмувати без std :: shared_ptr і все-таки керувати життям об'єктів безпечним способом?

C ++ має велику кількість різних способів створення пам'яті, наприклад:

  1. Використовуйте struct A { MyStruct s1,s2; };замість shared_ptr в межах класу. Це лише для просунутих програмістів, оскільки це вимагає, щоб ви зрозуміли, як працюють залежності, і вимагає здатності контролювати залежності, достатню для обмеження їх у дереві. Порядок класів у файлі заголовка є важливим аспектом цього. Здається, це використання вже є звичним для вбудованих типів c ++, але його використання з визначеними програмістами класами здається менш використаним через ці залежності та порядок класів. Це рішення також має проблеми з розміром. Програмісти вбачають у цьому проблеми як вимогу використовувати декларації вперед або непотрібні #includes, і тому багато програмістів повернуться до нижчого рішення покажчиків і пізніше до shared_ptr.
  2. Використовуйте MyClass &find_obj(int i);+ clone () замість shared_ptr<MyClass> create_obj(int i);. Багато програмістів хочуть створити заводи для створення нових об’єктів. shared_ptr ідеально підходить для такого типу використання. Проблема полягає в тому, що вона вже передбачає складне рішення управління пам’яттю, використовуючи розподіл heap / free store, замість більш простого рішення на основі стека чи об’єктів. Хороша ієрархія класів C ++ підтримує всі схеми управління пам’яттю, а не лише одну з них. Рішення на основі посилань може працювати, якщо повернутий об'єкт зберігається всередині об'єкта, що містить, замість використання змінної локальної функції функції. Слід уникати передачі права власності від заводу до коду користувача. Копіювання об'єкта після використання find_obj () - хороший спосіб поводження з ним - нормальні конструктори копій та звичайний конструктор (іншого класу) з параметром refrerence або clone () для поліморфних об’єктів можуть впоратися з ним.
  3. Використання посилань замість покажчиків або shared_ptrs. Кожен клас c ++ має конструктори, і кожен член посилальних даних потрібно ініціалізувати. Це використання дозволяє уникнути багатьох застосувань покажчиків та shared_ptrs. Вам просто потрібно вибрати, чи зберігається Ваша пам'ять всередині об'єкта чи поза ним, і вибрати структурне рішення або референтне рішення на основі рішення. Проблеми з цим рішенням зазвичай пов'язані з уникненням параметрів конструктора, що є загальною, але проблематичною практикою та нерозумінням того, як слід проектувати інтерфейси для класів.

"Слід уникати передачі права власності від заводу до коду користувача." А що станеться, коли це неможливо? "Використання посилань замість покажчиків або shared_ptrs." Гм, ні. Покажчики можна повторно налаштувати. Посилання не можуть. Це змушує обмежити час побудови на те, що зберігається в класі. Це не практично для багатьох речей. Здається, ваше рішення є дуже жорстким і негнучким до потреб більш текучого інтерфейсу та схеми використання.
Ніколь Болас

@Nicol Bolas: Після того, як ви будете дотримуватися наведених вище правил, відповідні джерела будуть використовуватися для залежностей між об'єктами, а не для зберігання даних, як ви запропонували. Залежності стабільніші за дані, тому ми ніколи не потрапляємо в проблему, яку ви розглядали.
tp1

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

@nicol bolas: о, це обробляється по-різному; інтерфейс класу підтримує більше однієї "сутності". Замість зіставлення 1: 1 між об'єктами та сутностями, ви будете використовувати сутність масиву. Тоді сутності гинуть дуже легко, просто видаляючи її з масиву. У всій грі лише невелика кількість сукупностей, і залежності між масивами змінюються не дуже часто :)
tp1

2
Ні, unique_ptrнайкраще підходить для заводів. Ви можете перетворити а unique_ptrна shared_ptr, але йти в іншому напрямку логічно неможливо.
Ben Voigt
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.