С++: универсальное использование shared_ptr <> эквивалентно gc?

Это только академический вопрос (я бы никогда не делал этого в реальном коде):

Если бы мне пришлось использовать shared_ptr < > универсально в моем коде, будет ли поведение эквивалентным gc-собранному языку, например Java?

Если нет, то как поведение будет отличаться от gc-внедренного языка? Какая конструкция С++ даст эквивалентное поведение по сравнению с gc-встроенным языком?

Примечание. В реальном кодировании я предпочитаю использовать RAII и строгое владение над использованием любых интеллектуальных указателей. Я также знаю, что другие менее общие указатели unique_ptr < > будут более эффективными. Этот вопрос - это просто запрос на эквивалентность интеллектуальных указателей.

Ответ 1

Нет, было бы несколько важных отличий:

  • Вы получите утечку памяти при любой циклической ссылке. Сборщик мусора может обрабатывать циклы, ref-counting не может.
  • Вы избегаете любых ларьков или пауз, потому что сбор мусора никогда не происходит. С другой стороны, вы, скорее всего, потратите больше времени на очистку ресурсов центрального процессора, потому что амортизируемая стоимость случайной сборки мусора довольно низкая, а подсчет ref-count может быть относительно дорогим, если вы делаете это на всех.

Очевидно, первая точка - убийца. Если вы это сделаете, многие из ваших ресурсов не будут освобождены, и вы будете утечки памяти, и ваше приложение просто не будет хорошо себя вести.

Какая конструкция С++ даст эквивалентное поведение по сравнению с gc-внедренным языком?

Отсутствует. У С++ нет сборщика мусора, потому что нет способа реализовать правильный, надежный. (Да, я знаю о GC Boehm, и это хорошее приближение, но оно консервативно и не обнаруживает всех ссылок, только те, на которые он может быть на 100% уверен. В общей программе на С++ нет способа, чтобы реализовать сборщик мусора, который Just Works (tm))

Ответ 2

@jalf говорит это в своем ответе:

Вы избегаете любых ларьков или пауз, потому что сбор мусора никогда не происходит.

В то время как интеллектуальные указатели (или любая схема подсчета ссылок) не имеют паузы во время сбора мусора, вы можете получить паузу, если вы обнуляете последний внешний указатель на большую структуру данных и запускаете каскад корректировок счетных ссылок и их финализации для каждый node в структуре данных. Хотя реализация умного умного указателя может улучшить это, вы пожертвуете немедленной мелиорацией..., которую некоторые люди утверждают, является преимуществом умных указателей.

Кроме того, каждый раз, когда вы назначаете интеллектуальную переменную с указателем, есть накладные расходы на несколько инструкций, и накладные расходы на выделение объекта больше.

Ответ 3

Сбор мусора происходит всякий раз, когда GC решает, что он должен. shared_ptr не собираются. Объект, управляемый shared_ptr, будет уничтожен только в деструкторе shared_ptr. И поэтому вы точно знаете, когда память может и не может быть освобождена.

У вас все еще есть контроль, когда память уходит с shared_ptr. У вас нет этого с сборщиком мусора (за пределами грубых команд, таких как включение/выключение или изменение его поведения).

Ответ 4

Основное отличие состоит в том, что подсчет ссылок не может свободно создавать круговые структуры данных.

Тем не менее многие случаи таких структур могут быть обработаны с помощью weak_ptr соответствующим образом, и некоторые случаи могут быть обработаны делегированием ответственности очистки для объекта коллекции.

Однако самые легкомысленные структуры спагетти, если вы хотите их (например, для математики), не могут иметь автоматическую очистку, реализованную только подсчетом ссылок, потому что там будут круговые подструктуры.

Приветствия и hth.,

Ответ 5

Стоит отметить, что общий ptr намного больше, чем ссылка Java. Как правило, это не имеет значения, кроме как в некоторых ситуациях.

В Java 6 64-разрядные JVM по-прежнему используют 32-битные ссылки на доступ до 32 ГБ кучи (это может быть сделано, потому что объекты находятся на границах 8 байтов). Однако общий ptr использует два указателя (каждый 8 байтов в 64-битные приложения), второй указатель ссылается на объект, содержащий счетчик. В libgcc он выделяет 32-байтовый минимум любому malloc/new object. В общей сложности общий указатель может использовать 48 байтов, который относительно больше 4 байтов. 44 байта не будет иметь никакого значения, но это может быть, если у вас их много.