Что происходит с мусором в C ++?

51

У Java есть автоматический сборщик мусора, который время от времени останавливает мир, но заботится о мусоре в куче. Теперь приложения на C / C ++ не имеют таких зависаний STW, их использование памяти также не увеличивается бесконечно. Как достигается это поведение? Как ухаживать за мертвыми предметами?

Джу Шуа
источник
38
Примечание: остановка мира - это выбор реализации некоторых сборщиков мусора, но, конечно, не всех. Например, существуют параллельные GC, которые работают одновременно с мутатором (это то, что разработчики GC называют реальной программой). Я полагаю, что вы можете купить коммерческую версию IBM JVM J9 с открытым исходным кодом, которая имеет параллельный без пауз сборщик. Azul Zing имеет «без пауз» коллектор, который на самом деле не является паузой, но очень быстр, так что нет заметных пауз (его паузы GC находятся в том же порядке, что и переключение контекста потока операционной системы, которое обычно не рассматривается как пауза) ,
Jörg W Mittag
14
Большинство (долгоиграющая) программы на C ++ , которые я использую сделать есть использование памяти , которая растет неограниченно по времени. Возможно ли, что вы не привыкли оставлять программы открытыми более чем на несколько дней?
Джонатан В ролях
12
Учтите, что с современным C ++ и его конструкциями вам больше не нужно удалять память вручную (если только вы не нуждаетесь в особой оптимизации), потому что вы можете управлять динамической памятью с помощью интеллектуальных указателей. Очевидно, что это добавляет некоторые накладные расходы к разработке на C ++, и вам нужно быть немного более осторожным, но это не совсем другая вещь, вам просто нужно помнить, чтобы использовать умную конструкцию указателя вместо простого вызова manual new.
Энди
9
Обратите внимание, что утечки памяти все еще возможны на языке, который собирает мусор. Я незнаком с Java, но утечки памяти, к сожалению, довольно распространены в управляемом мире GC .NET. Объекты, на которые косвенно ссылается статическое поле, не собираются автоматически, обработчики событий являются очень распространенным источником утечек, а недетерминированный характер сборки мусора делает невозможным полное устранение необходимости вручную освобождать ресурсы (что приводит к IDisposable шаблон). В общем, правильно используемая модель управления памятью C ++ намного превосходит сборщик мусора.
Коди Грей
26
What happens to garbage in C++? Разве это обычно не компилируется в исполняемый файл?
BJ Myers

Ответы:

100

Программист несет ответственность за newудаление объектов, созданных с помощью delete. Если объект создан, но не уничтожен до того, как последний указатель или ссылка на него выйдут из области видимости, он провалится через трещины и станет утечкой памяти .

К сожалению, для C, C ++ и других языков, которые не включают GC, это просто накапливается со временем. Это может привести к тому, что приложению или системе не хватит памяти, и они не смогут выделить новые блоки памяти. На этом этапе пользователь должен прибегнуть к прекращению работы приложения, чтобы операционная система могла вернуть использованную память.

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

int main()
{
    int* variableThatIsAPointer = new int;
    int variableInt = 0;

    delete variableThatIsAPointer;
}

Здесь мы создали две переменные. Они существуют в Block Scope , как определено {}фигурными скобками. Когда выполнение выходит из этой области, эти объекты будут автоматически удалены. В этом случае, variableThatIsAPointerкак следует из его названия, указатель на объект в памяти. Когда он выходит из области видимости, указатель удаляется, но объект, на который он указывает, остается. Здесь мы deleteрассматриваем этот объект до того, как он выходит из области видимости, чтобы гарантировать отсутствие утечки памяти. Однако мы могли бы также передать этот указатель в другом месте и ожидать, что он будет удален позже.

Эта природа области действия распространяется на классы:

class Foo
{
public:
    int bar; // Will be deleted when Foo is deleted
    int* otherBar; // Still need to call delete
}

Здесь применяется тот же принцип. Нам не нужно беспокоиться о том, barкогда Fooудаляется. Однако для otherBar, только указатель удаляется. Если otherBarэто единственный действительный указатель на какой-либо объект, на который он указывает, мы, вероятно, должны использовать deleteего в Fooдеструкторе. Это движущая концепция RAII

распределение ресурсов (получение) выполняется во время создания объекта (в частности, инициализации) конструктором, а освобождение ресурса (освобождение) - в процессе уничтожения объекта (в частности, финализации) деструктором. Таким образом, ресурс гарантированно удерживается между окончанием инициализации и началом финализации (удержание ресурсов является инвариантом класса) и удерживается только тогда, когда объект жив. Таким образом, если нет утечек объектов, нет утечек ресурсов.

RAII также является типичной движущей силой Smart Pointers . В стандартной библиотеке C ++, это std::shared_ptr, std::unique_ptrи std::weak_ptr; хотя я видел и использовал другие shared_ptr/ weak_ptrреализации, которые следуют тем же понятиям. Для них счетчик ссылок отслеживает, сколько указателей существует на данный объект, и автоматически получает объект, deleteкогда на него больше нет ссылок.

Помимо этого, все сводится к надлежащей практике и дисциплине для программиста, чтобы гарантировать, что их код обрабатывает объекты должным образом.

Thebluefish
источник
4
удалил через delete- вот что я искал. Потрясающие.
Джу Шуа
3
Возможно, вы захотите добавить о механизмах определения объема, предоставляемых в c ++, которые позволяют делать большую часть нового и удаления в основном автоматически.
whatsisname
9
@whatsisname это не то, что новые и удаления сделаны автоматически, это то, что они вообще не происходят во многих случаях
Caleth
10
deleteАвтоматически вызывается для вас смарт - указатели , если вы используете их , так что вы должны рассмотреть возможность использования их каждый раз , когда автоматическое хранение не может быть использован.
Мариан Спаник
11
@JuShua Обратите внимание, что при написании современного C ++ вам не нужно вообще иметь deleteкод приложения (и начиная с C ++ 14 и далее, то же самое с new), а вместо этого использовать умные указатели и RAII для удаления объектов кучи. std::unique_ptrТип и std::make_uniqueфункция являются прямой, самой простой заменой newи deleteна уровне кода приложения.
Hyde
82

C ++ не имеет сборки мусора.

Приложения C ++ должны утилизировать свой мусор.

Разработчики приложений C ++ должны понимать это.

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

Джон Р. Штром
источник
22
Вы, безусловно, убедились, что ваш ответ не содержит ни мусора, ни шаблон ...
leftaroundabout
15
@leftaroundabout: Спасибо. Я считаю это комплиментом.
Джон Р. Штром
1
Хорошо, у этого ответа без мусора есть ключевое слово для поиска: утечка памяти. Также было бы неплохо как-то упомянуть newи delete.
Руслан
4
@Ruslan То же самое относится и к mallocи free, или new[]и delete[], или любым другим распределителям (как для Windows - х GlobalAlloc, LocalAlloc, SHAlloc, CoTaskMemAlloc, VirtualAlloc, HeapAlloc, ...), и память , выделенная для вас (например , с помощью fopen).
user253751
43

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

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

int global = 0; // automatic storage

int foo(int a, int b) {
    static int local = 1; // automatic storage

    int c = a + b; // automatic storage

    return c;
}

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

При использовании динамического хранения в C память традиционно выделяется mallocи восстанавливается с помощью free. В C ++ память традиционно выделяется newи восстанавливается с помощью delete.

C не сильно изменился за эти годы, однако современный C ++ избегает newи deleteполностью и полагается вместо этого на библиотечные средства (которые сами используют newи deleteсоответственно):

  • умные указатели являются самыми известными: std::unique_ptrиstd::shared_ptr
  • но контейнеры гораздо более широкое распространение на самом деле: std::string, std::vector, std::map, ... все внутри управления динамически выделенной памяти прозрачно

Говоря о том shared_ptr, что существует риск: если цикл ссылок сформирован, а не нарушен, то утечка памяти может быть. Разработчик должен избежать этой ситуации, самый простой способ - shared_ptrполностью избежать , а второй - избежать циклов на уровне типа.

В результате утечки памяти не являются проблемой в C ++ даже для новых пользователей, если они не используют их new, deleteили std::shared_ptr. Это не похоже на C, где необходима стойкая дисциплина и, как правило, недостаточно.


Тем не менее, этот ответ не будет полным без упоминания сестры-близнеца утечки памяти: висячие указатели .

Висячий указатель (или свисающая ссылка) - это опасность, создаваемая сохранением указателя или ссылки на мертвый объект. Например:

int main() {
    std::vector<int> vec;
    vec.push_back(1);     // vec: [1]

    int& a = vec.back();

    vec.pop_back();       // vec: [], "a" is now dangling

    std::cout << a << "\n";
}

Использование висячего указателя или ссылки является неопределенным поведением . В общем, к счастью, это немедленный сбой; довольно часто, к сожалению, это сначала вызывает повреждение памяти ... и время от времени возникает странное поведение, потому что компилятор выдает действительно странный код.

Неопределенное поведение - самая большая проблема с C и C ++ на сегодняшний день, с точки зрения безопасности / правильности программ. Возможно, вы захотите проверить Rust для языка без сборщика мусора и без неопределенного поведения.

Матье М.
источник
17
Re: «Использование висячего указателя или ссылки, является неопределенным поведением . В общем, к счастью, это немедленный сбой»: Правда? Это совсем не соответствует моему опыту; напротив, мой опыт показывает, что использование висящего указателя почти никогда не вызывает немедленного сбоя. , ,
Руах
9
Да, так как указатель «висящий» должен быть ориентирован на ранее выделенную память в одной точке, и эта память, как правило, вряд ли будет полностью отключена от процесса, так что она вообще больше не будет доступна, потому что это будет хороший кандидат для немедленного повторного использования ... на практике висячие указатели не вызывают сбоев, они вызывают хаос.
Леушенко
2
«В результате утечки памяти не являются проблемой в C ++». Конечно, всегда есть привязки C к библиотекам, которые можно испортить, а также рекурсивные shared_ptrs или даже рекурсивные unique_ptrs и другие ситуации.
Mooing Duck
3
«Не проблема в C ++, даже для новых пользователей» - я бы отнес это к «новым пользователям, которые не пришли из Java-подобного языка или C ».
оставлено около
3
@leftaroundabout: он квалифицирован "до тех пор, пока они воздерживаются от использования new, deleteи shared_ptr"; без newи у shared_ptrвас есть прямое владение, поэтому нет утечек. Конечно, у вас могут быть висячие указатели и т. Д., Но я боюсь, что вам нужно покинуть C ++, чтобы избавиться от них.
Матье М.
27

В C ++ есть такая вещь, как RAII . В основном это означает, что мусор убирается, когда вы идете, а не оставляете его в куче и позволяете уборщику убирать за вами. (представьте, что я в своей комнате смотрю футбол - пока я пью банки с пивом и мне нужны новые, путь C ++ - это подносить пустую банку в мусорное ведро по пути к холодильнику, а C # - бросать ее на пол и ждать, пока горничная поднимет их, когда придет чистить).

Теперь в C ++ возможна утечка памяти, но для этого необходимо оставить обычные конструкции и вернуться к способу выполнения действий на C - выделению блока памяти и отслеживанию того, где этот блок находится без какой-либо помощи языка. Некоторые люди забывают этот указатель и поэтому не могут удалить блок.

gbjbaanb
источник
9
Общие указатели (которые используют RAII) предоставляют современный способ создания утечек. Предположим, что объекты A и B ссылаются друг на друга через общие указатели, и ничто иное не ссылается на объект A или объект B. В результате возникает утечка. Эта взаимная ссылка не является проблемой в языках с сборкой мусора.
Дэвид Хаммен,
@DavidHammen конечно, но ценой обхода почти каждого объекта, чтобы убедиться. Ваш пример умных указателей игнорирует тот факт, что сам умный указатель выйдет из области видимости, а затем объекты будут освобождены. Вы предполагаете, что «умный» указатель похож на указатель, а не «объект», который передается в стеке, как большинство параметров. Это мало чем отличается от утечек памяти, вызванных языками GC. например, известный, где удаление обработчика событий из класса пользовательского интерфейса оставляет его без ссылки и, следовательно, с утечкой.
gbjbaanb
1
@gbjbaanb в примере с умными указателями, ни один умный указатель никогда не выходит из области видимости, поэтому есть утечка. Поскольку оба объекта интеллектуального указателя размещены в динамическом , а не в лексическом контексте, каждый из них пытается подождать другого перед разрушением. Факт, что интеллектуальные указатели являются реальными объектами в C ++, а не просто указателями, это именно то, что вызывает утечку - дополнительные объекты интеллектуальных указателей в областях стека, которые также указывают на объекты-контейнеры, не могут освободить их, когда они уничтожают себя, потому что refcount является ненулевая.
Леушенко
2
Путь .NET - не бросать его на пол. Он просто держит его там, где он был, пока горничная не придет. А благодаря тому, как .NET распределяет память на практике (не по контракту), куча больше похожа на стек с произвольным доступом. Это все равно что иметь пачку контрактов и бумаг и время от времени просматривать их, чтобы отбросить те, которые больше не действительны. И чтобы сделать это проще, те, которые выживают после каждого сброса, переводятся в другой стек, так что вы можете избежать обхода всех стеков большую часть времени - пока первый стек не станет достаточно большим, горничная не коснется остальных.
Луаан
@Luaan, это была аналогия ... Я думаю, ты был бы счастлив, если бы я сказал, что на столе остаются банки, пока горничная не приберется, чтобы вымыться.
gbjbaanb
26

Следует отметить, что в случае с C ++ распространенным заблуждением является то, что «вам необходимо выполнить ручное управление памятью». Фактически, вы обычно не управляете памятью в своем коде.

Объекты фиксированного размера (с продолжительностью жизни)

В подавляющем большинстве случаев, когда вам нужен объект, он будет иметь определенное время жизни в вашей программе и будет создан в стеке. Это работает для всех встроенных примитивных типов данных, но также для экземпляров классов и структур:

class MyObject {
    public: int x;
};

int objTest()
{
    MyObject obj;
    obj.x = 5;
    return obj.x;
}

Стековые объекты автоматически удаляются при завершении функции. В Java объекты всегда создаются в куче, и поэтому должны быть удалены каким-либо механизмом, например сборкой мусора. Это не проблема для объектов стека.

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

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

class MyList {        
public:
    // a fixed-size pointer to the actual memory.
    int* listOfInts; 
    // constructor: get memory
    MyList(size_t numElements) { listOfInts = new int[numElements]; }
    // destructor: free memory
    ~MyList() { delete[] listOfInts; }
};

int listTest()
{
    MyList list(1024);
    list.listOfInts[200] = 5;
    return list.listOfInts[200];
    // When MyList goes off stack here, its destructor is called and frees the memory.
}

В коде, где используется память, управление памятью вообще отсутствует. Единственное, что нам нужно, это убедиться, что у написанного нами объекта есть подходящий деструктор. Независимо от того, как мы покидаем область действия listTest, будь то через исключение или просто возвращаясь из него, вызывается деструктор ~MyList(), и нам не нужно управлять какой-либо памятью.

(Я думаю, что это забавное дизайнерское решение использовать двоичный оператор NOT~ для обозначения деструктора. При использовании над числами он инвертирует биты; по аналогии, здесь он указывает, что то, что сделал конструктор, инвертировано.)

В основном все объекты C ++, которым требуется динамическая память, используют эту инкапсуляцию. Он был назван RAII («получение ресурсов - инициализация»), что является довольно странным способом выразить простую идею о том, что объекты заботятся о своем собственном содержимом; что они приобретают, так это их убирать.

Полиморфные объекты и время жизни за пределами видимости

Теперь оба этих случая касались памяти с четко определенным временем жизни: время жизни совпадает с областью действия. Если мы не хотим, чтобы срок действия объекта истек при выходе из области действия, существует третий механизм, который может управлять памятью для нас: умный указатель. Умные указатели также используются, когда у вас есть экземпляры объектов, тип которых меняется во время выполнения, но которые имеют общий интерфейс или базовый класс:

class MyDerivedObject : public MyObject {
    public: int y;
};
std::unique_ptr<MyObject> createObject()
{
    // actually creates an object of a derived class,
    // but the user doesn't need to know this.
    return std::make_unique<MyDerivedObject>();
}

int dynamicObjTest()
{
    std::unique_ptr<MyObject> obj = createObject();
    obj->x = 5;
    return obj->x;
    // At scope end, the unique_ptr automatically removes the object it contains,
    // calling its destructor if it has one.
}

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

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

Считается крайне плохой практикой использовать необработанные указатели в качестве владельцев ресурсов где-либо в коде C ++, необработанные выделения вне конструкторов и необработанные deleteвызовы вне деструкторов, поскольку ими практически невозможно управлять при возникновении исключений и, как правило, трудно безопасно использовать.

Лучшее: это работает для всех типов ресурсов

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

Например, функция, блокирующая мьютекс, обычно пишется так в C ++:

void criticalSection() {
    std::scoped_lock lock(myMutex); // scoped_lock locks the mutex
    doSynchronizedStuff();
} // myMutex is released here automatically

Другие языки делают это намного более сложным, требуя, чтобы вы делали это вручную (например, в finallyпредложении), или они порождают специализированные механизмы, которые решают эту проблему, но не особенно изящно (обычно позже в их жизни, когда достаточно людей пострадал от недостатка). Такие механизмы - это try-with-resources в Java и оператор using в C #, оба они являются приближением RAII C ++.

Подводя итог, можно сказать, что все это было очень поверхностным описанием RAII в C ++, но я надеюсь, что это поможет читателям понять, что управление памятью и даже ресурсами в C ++ обычно не «ручное», а фактически автоматическое.

Феликс Домбек
источник
7
Это единственный ответ, который не дезинформирует людей и не рисует C ++ сложнее или опаснее, чем он есть на самом деле.
Александр Рево
6
Кстати, считается плохой практикой использование необработанных указателей в качестве владельцев ресурсов. Нет ничего плохого в том, чтобы использовать их, если они указывают на то, что гарантированно переживет сам указатель.
Александр Рево
8
Я второй Александр. Я озадачен, увидев, что «C ++ не имеет автоматического управления памятью, забудь, deleteи ты мертв», ответы набирают более 30 баллов и получают признание, а у этого пять. Кто-нибудь на самом деле использует C ++ здесь?
Квентин
8

Что касается C, то этот язык не дает вам инструментов для управления динамически выделяемой памятью. Вы несете полную ответственность за то, чтобы у каждого *allocбыло что- freeто соответствующее .

Когда вещи становятся действительно неприятными, это когда распределение ресурсов не удается на полпути; Вы пытаетесь снова, вы откатываетесь и начинаете сначала, вы откатываетесь и выходите с ошибкой, вы просто освобождаетесь от обязательств и позволяете ОС справиться с этим?

Например, вот функция для выделения несмежного 2D-массива. Поведение здесь таково, что если сбой выделения происходит в середине процесса, мы откатываем все назад и возвращаем индикацию ошибки, используя указатель NULL:

/**
 * Allocate space for an array of arrays; returns NULL
 * on error.
 */
int **newArr( size_t rows, size_t cols )
{
  int **arr = malloc( sizeof *arr * rows );
  size_t i;

  if ( arr ) // malloc returns NULL on failure
  {
    for ( i = 0; i < rows; i++ )
    {
      arr[i] = malloc( sizeof *arr[i] * cols );
      if ( !arr[i] )
      {
        /**
         * Whoopsie; we can't allocate any more memory for some reason.
         * We can't just return NULL at this point since we'll lose access
         * to the previously allocated memory, so we branch to some cleanup
         * code to undo the allocations made so far.  
         */
        goto cleanup;
      }
    }
  }
  goto done;

/**
 * We encountered a failure midway through memory allocation,
 * so we roll back all previous allocations and return NULL.
 */
cleanup:
  while ( i )         // this is why we didn't limit the scope of i to the for loop
    free( arr[--i] ); // delete previously allocated rows
  free( arr );        // delete arr object
  arr = NULL;

done:
  return arr;
}

Этот код неприятен с этими gotos, но, при отсутствии какого-либо механизма структурированной обработки исключений, это практически единственный способ справиться с проблемой, не выполняя полную выручку, особенно если ваш код распределения ресурсов вложен больше чем одна петля глубиной. Это один из немногих случаев, когда gotoна самом деле привлекательный вариант; в противном случае вы используете кучу флагов и дополнительных ifоператоров.

Вы можете облегчить себе жизнь, написав выделенные функции распределителя / освобождения для каждого ресурса, что-то вроде

Foo *newFoo( void )
{
  Foo *foo = malloc( sizeof *foo );
  if ( foo )
  {
    foo->bar = newBar();
    if ( !foo->bar ) goto cleanupBar;
    foo->bletch = newBletch(); 
    if ( !foo->bletch ) goto cleanupBletch;
    ...
  }
  goto done;

cleanupBletch:
  deleteBar( foo->bar );
  // fall through to clean up the rest

cleanupBar:
  free( foo );
  foo = NULL;

done:
  return foo;
}

void deleteFoo( Foo *f )
{
  deleteBar( f->bar );
  deleteBletch( f->bletch );
  free( f );
}
Джон Боде
источник
1
Это хороший ответ, даже с gotoзаявлениями. Это рекомендуемая практика в некоторых областях. Это часто используемая схема защиты от эквивалента исключений в C. Посмотрите на код ядра Linux, который полон gotoоператоров - и который не пропускает.
Дэвид Хаммен,
«без полной помощи» -> если честно, если вы хотите поговорить о C, это, вероятно, хорошая практика. C - это язык, который лучше всего использовать для обработки блоков памяти, пришедших откуда-то еще, или для выделения небольших кусков памяти другим процедурам, но предпочтительно не выполнять их одновременно с чередованием. Если вы используете классические «объекты» в C, вы, вероятно, не используете язык в его сильных сторонах.
Леушенко
Второй gotoпосторонний. Было бы более читаемым , если вы изменили goto done;в return arr;и arr=NULL;done:return arr;к return NULL;. Хотя в более сложных случаях действительно может быть несколько gotos, начинающих развертываться с разными уровнями готовности (что будет сделано при размотке стека исключений в C ++).
Руслан
2

Я научился классифицировать проблемы с памятью на несколько разных категорий.

  • Один раз капает. Предположим, что программа пропускает 100 байт во время запуска, но никогда больше не будет. Поиск и устранение этих разовых утечек - это хорошо (мне нравится иметь чистый отчет с помощью функции обнаружения утечек), но это не обязательно. Иногда возникают более серьезные проблемы, которые нужно атаковать.

  • Повторные утечки. Функция, которая вызывается многократно в течение жизненного цикла программы и регулярно теряет память, является большой проблемой. Эти потеки замучат программу и, возможно, ОС до смерти.

  • Взаимные ссылки. Если объекты A и B ссылаются друг на друга через общие указатели, вы должны сделать что-то особенное, либо в дизайне этих классов, либо в коде, который реализует / использует эти классы, чтобы разорвать цикличность. (Это не проблема для языков с мусором.)

  • Вспоминая слишком много. Это злая двоюродная сестра мусора / утечки памяти. RAII здесь не поможет и не будет собирать мусор. Это проблема на любом языке. Если какая-то активная переменная имеет путь, который соединяет ее с каким-то случайным фрагментом памяти, этот случайный фрагмент памяти не является мусором. Сделать программу забывчивой, чтобы она могла работать в течение нескольких дней, сложно. Создание программы, которая может работать несколько месяцев (например, до тех пор, пока диск не выйдет из строя) очень и очень сложно.

У меня не было серьезных проблем с утечками в течение долгого времени. Использование RAII в C ++ очень помогает в устранении этих проблем. (Однако следует соблюдать осторожность с общими указателями.) Гораздо важнее, что у меня были проблемы с приложениями, использование памяти которых продолжает расти и расти, и растет из-за неразрывных соединений с памятью, которые больше не нужны.

Дэвид Хаммен
источник
-6

Программист C ++ должен реализовать свою собственную форму сборки мусора, где это необходимо. Невыполнение этого требования приведет к так называемой «утечке памяти». В языках «высокого уровня» (таких как Java) довольно часто встроена сборка мусора, в отличие от языков «низкого уровня», таких как C и C ++.

xDr_Johnx
источник