September 4, 2018

.cpp memory leak

Век живи, век учись. Сегодня наткнулся на неожиданное явление в С++, которое может привести к утечкам памяти. Пишу для себя, дальше можно не читать.

Дано три файла, Файл1 - тело программы, Файл2 - описание класса "Класс1", Файл3 - в нём содержится Функция1. Класс1 состоит из конструктора, в котором выделяется память, и деструктора, в котором эта память освобождается. Например:

class Class1{
public:
char * str1;
Class1(){
str1 = new char[100];
}
~Class1(){
delete [] str1;
}
};

Основная программа в Файле1 содержит указатель на экземпляр Класса1, при этом, структуры класса не знает. Например, вот так:

public:
class Class1 * c;

(т.е. где-то есть некий класс, а здесь лежит на него ссылка)

Файл3 знает и о структуре основной программы, и о структуре Класса1. Функция1 создаёт экземпляр Класса1 (при этом вызывается конструктор класса, выделяющий память, в нашем примере 100 байт), и записывает ссылку в объект основной программы. Например, вот так:

#include "Файл1.h"
#include "Файл2.h"
void Функция1(){
Файл1->c = new Class1();
}

Основная программа в Файле1 сначала вызывает Функцию1 (которая создаёт объект), а в конце удаляет объект, вот так:

delete c;

Теперь, важный момент! Если в основной программе "Файл1" нет ссылки на описание класса (#include "Файл2.h"), то при удалении объекта (delete c) освобождаются 4 байта указателя, но деструктор Класса1 _не вызывается_, и происходит утечка памяти (эти наши 100 байт). Дебагер зарегистрирует Memory Leak, и это легко проверить, поставив брейкпоинт в теле деструктора - он никогда не будет вызван.



Логично, программа не знает о наличии деструктора у класса, но это совершенно не очевидно!

При этом, пример компилируется без ошибок, и намёков на возможную проблему никаких не даёт. Стоит лишь добавить в Файл1 строчку #include "Файл2.h", как проблема исчезает, деструктор будет вызван, и память очистится.

Понимаете? Здесь явная асимметрия. Компилятор не пропустит код, если программа не знает о структуре класса в процессе создания объекта. Но свободно пропускает код, в котором есть "слепое" удаление. Гендерное неравноправие констукторов и деструкторов, о котором я раньше и не подозревал.

Посвящаю этот пост трём часам времени, потерянным сегодня при ловле Memory Leak.

No comments: