Деструкторы в C++ с использованием векторов и указателей
У меня есть некоторые вопросы касательно управления памятью в C++. Я знаю, что в деструкторах нужно освобождать память для всего, что было создано с помощью new
, а также закрывать открытые потоки файлов и другие потоки. Однако у меня есть сомнения относительно других объектов в C++:
Автоматическое разрушение: Автоматически ли уничтожаются объекты
std::vector
иstd::string
?Векторы указателей: Рассмотрим следующий код:
std::vector<myClass*>
Если у меня есть вектор указателей на объекты класса
myClass
, что произойдет, когда будет вызван деструктор вектора? Будет ли автоматически вызван деструкторmyClass
для каждого объекта, или только уничтожится сам вектор, а объекты, на которые он указывает, останутся в памяти?Указатели внутри классов: Что произойдет, если у меня есть указатель на другой класс внутри одного класса, например:
class A { ClassB* B; }
Если класс
A
будет уничтожен в какой-то момент в коде, будет ли также уничтожен классB
, или уничтожится только указатель, а классB
останется где-то в памяти?
Спасибо за помощь!
5 ответ(ов)
Да, они уничтожаются автоматически (при условии, что членские переменные не являются указателями на std::vector
и std::string
).
Если у вас есть что-то вроде std::vector
, что происходит, когда вызывается деструктор вектора? Вызывается ли автоматически деструктор вашего класса? Или уничтожается только вектор, а все объекты, которые он содержит, остаются в памяти?
Если это vector<MyClass>
, то все объекты, содержащиеся в векторе, будут уничтожены. Если это vector<MyClass*>
, то все объекты должны быть явным образом удалены с помощью delete
(при условии, что класс, в котором находится вектор, владеет этими объектами). Третья альтернатива — это использовать вектор умных указателей, например, vector<shared_ptr<MyClass>
, в этом случае элементы вектора не нужно будет явно удалять.
Что произойдет, если внутри класса есть указатель на другой класс?
Указатель B
должен быть явно удален с помощью delete
. Опять же, можно использовать умный указатель для управления уничтожением B
.
Когда вы работаете с динамически выделенной памятью в C++ (например, когда вы используете оператор new
), вам нужно заботиться о её освобождении, чтобы избежать утечек памяти.
В приведённом вами примере класса Myclass
используется динамический массив символов, который выделяется с помощью оператора new
. Важно правильно освобождать эту память в деструкторе класса, чтобы избежать утечек.
Пример:
class Myclass {
private:
char* ptr; // Указатель на динамически выделенный массив
public:
// Деструктор, освобождающий память
~Myclass() {
delete[] ptr; // Освобождаем память
}
};
Таким образом, вам нужно беспокоиться о том, чтобы добавить delete
или delete[]
(в зависимости от того, как вы выделяли память) в деструктор, чтобы гарантировать, что память будет освобождена при уничтожении объекта. Если вы не сделаете этого, вы можете столкнуться с утечками памяти, что повредит стабильности и производительности вашего приложения.
Если объекты находятся в автоматическом хранилище, то да. Вы можете создать указатель на std::string
, используя std::string* s = new std::string
, в этом случае вы должны самостоятельно освобождать память с помощью delete
.
Ничего особенного, вы должны вручную освобождать память, которую вы выделили (при помощи new
).
Если вы выделили b
с помощью new
, то вам следует явно уничтожить его в деструкторе.
Хорошее правило: используйте delete/delete[]
для каждого new/new[]
, который есть в вашем коде.
Лучшее правило: используйте RAII и предпочитайте умные указатели вместо "сырых" указателей.
Это зависит от контекста. std::vector
, std::string
и MyClass
имеют одно общее свойство: если вы объявляете переменную любого из этих типов, она будет выделена в стеке, будет локальной для текущего блока, в котором вы находитесь, и будет разрушена, когда блок завершится.
Например:
{
std::vector<std::string> a;
std::string b;
MyClass c;
} // в этот момент сначала будет разрушен объект c, затем b, затем все строки в a, и наконец сам вектор a.
Если перейти к указателям, то вы оказались правы: только память, занимаемая самим указателем (обычно это 4-байтное целое число), будет автоматически освобождена при выходе из области видимости. Ничего не произойдет с памятью, на которую указывает указатель, если вы явно не используете delete
(независимо от того, находится ли этот указатель в векторе или нет). Если у вас есть класс, который содержит указатели на другие объекты, вам может потребоваться освободить их в деструкторе (в зависимости от того, является ли этот класс "владельцем" этих объектов). Обратите внимание, что в C++11 появились классы указателей (называемые умными указателями), которые позволяют вам работать с указателями аналогично "обычным" объектам:
Пример:
{
std::unique_ptr<std::string> a = std::make_unique<std::string>("Hello World");
function_that_wants_string_ptr(a.get());
} // в этом случае a вызовет delete для своего внутреннего указателя на строку и затем будет разрушен
Это зависит от ситуации.
Если у вас есть вектор значений std::vector<MyClass>
, то деструктор вектора вызовет деструктор для каждого экземпляра MyClass
, находящегося в векторе.
Если у вас есть вектор указателей std::vector<MyClass*>
, то вы несете ответственность за удаление экземпляров MyClass
.
Что касается вопроса о том, что происходит, если внутри класса есть указатель на другой класс, то экземпляр ClassB
останется в памяти. Возможные способы, чтобы деструктор ClassA
выполнил эту задачу за вас, — это сделать B
членом класса или использовать умный указатель.
Использование `destructor = delete;` в C++
Как удалить элемент из std::vector<> по индексу?
`unsigned int` против `size_t`: когда и что использовать?
Какова разница между "new", "malloc" и "calloc" в C++?
Что означает && в конце сигнатуры функции (после закрывающей скобки)?