Причина утечки в том, что конструктор Screen
динамически распределяет память, используя два выражения new
, а соответствующее выражение delete
отсутствует.
По этой причине простоефункция типа
void f()
{
Screen x(some_width, some_height);
// x is destructed as f() returns
}
вызовет утечку, поскольку память, выделенная конструктором Screen
s, никогда не будет освобождена.
Одним из решений является добавление соответствующих выражений delete
в деструкторНапример, (в пределах определения класса)
~Screen()
{
delete [] m_color;
delete [] m_data;
};
Имейте в виду, что у класса могут быть другие конструкторы - и некоторые конструкторы автоматически генерируются компилятором, если вы не предотвращаете это (чего у вас нет).Необходимо обеспечить, чтобы все конструкторы выделяли память одинаковым образом, чтобы избежать неопределенного поведения.Например, сгенерированный по умолчанию конструктор копирования будет копировать указатели, а не делать динамическое выделение памяти.Поэтому его использование приводит к двум экземплярам Screen
, содержащим указатели на одну и ту же динамически распределенную память.Когда оба объекта впоследствии будут уничтожены, общая память будет освобождена дважды.Тогда поведение не определено.
Лучшим вариантом является избегать использования необработанных указателей и использовать стандартные контейнеры.Например, присвойте m_color
тип std::vector<TerminalColor>
и m_data
тип std::vector<char>
и инициализируйте их в конструкторе, используя что-то вроде
Screen(uint16_t width, uint16_t height)
: m_width(width), m_height(height),
m_size(width*height),
m_color(m_size), // initialise m_color to have m_size elements
m_data(m_size) // initialise m_data to have m_size elements
{}
Преимущество использования стандартных контейнеров заключается в том, что вам не нужносделать что-нибудь в деструкторе Screen
s, потому что память будет освобождена автоматически.Это также гарантирует, что другие конструкторы воспроизводятся правильно (т.е. избегают утечек памяти), если вы не сделаете все возможное, чтобы изменить поведение этих конструкторов
Я выбрал std::vector
в качестве стандартного типа контейнера.В зависимости от потребностей, вы можете использовать другие типы контейнеров.
Один из компромиссов с использованием std::vector
заключается в том, что вам МОЖЕТ потребоваться внести незначительные изменения в способ доступа к элементам массивов (например, если вам нужночтобы передать указатель на первый элемент m_color
функции, ожидающей массив, передайте &m_color[0]
вместо m_color
), но эти настройки просты.