Утечка памяти С++, не могу найти где

Я использую Visual Studio 2008, разрабатываю окно OpenGL. Я создал несколько классов для создания скелета, один для суставов, один для кожи, один для тела (который является держателем для нескольких суставов и кожи) и один для чтения файла скелета/скина.

В каждом из моих классов я использую указатели для большинства своих данных, большинство из которых объявлены с помощью = new int[XX]. У меня есть деструктор для каждого класса, который удаляет указатели, используя delete[XX].

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

Я не уверен, что это что-то в функции отображения перенасыщения, которая просто не удаляет класс Body, или что-то еще. Я выполнил шаги по обнаружению утечки памяти в Visual Studio 2008, и он не сообщает об утечке, но я не уверен на 100%, что он работает правильно для меня. Я не владею С++, поэтому, возможно, я что-то упускаю из виду, может кто-нибудь это увидеть?

Из основного:

void display(void){
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    Body *body = new Body();
    body->readSkel("C:\\skel2.skel");
    body->drawBody();
    body = new Body();
    body->readSkel("C:\\skel1.skel");
    body->drawBody();
    glutSwapBuffers();  
    body->~Body();
    delete body;
}

Из тела:

Body::Body(){
    skelFile = string();
    skinFile = string();
    totalJoints = 0;
    joints = new Joint[25];
    skin = new Skin;
}

Body::~Body(){
    delete[25] joints;
    delete skin; 
}

person Nicholas    schedule 08.04.2010    source источник
comment
Пожалуйста, прекратите программировать и прочитайте третье издание «Эффективного C++» Скотта Мейерса. Я не хочу быть язвительным, но у меня возникает соблазн создать длинный ответ, просто указав на некоторые из ваших ошибок в кодировании и плохих практиках. Книга Скотта Мейерса поможет вам улучшить код. В частности, пункты 4 и 14.   -  person deft_code    schedule 08.04.2010
comment
Сделаю. Извините, я давно не занимался C++, в нашей школе теперь преподают только Java (отстой, правда?), и учитель заставляет нас использовать C/C++, ничего не показывая нам сначала. I №3 Академия.   -  person Nicholas    schedule 12.04.2010


Ответы (4)


В этом коде:

Body *body = new Body();
body->readSkel("C:\\skel2.skel");
body->drawBody();
body = new Body();

вы сливаете Body, потому что не удаляете первый.

И это:

body->~Body();
delete body;

просто странно. Вы не вызываете такие деструкторы явно - delete заботится о вызове деструктора.

Этот код:

delete[25] joints;

тоже странно. Правильная форма:

delete [] joints;

Вы используете нестандартный синтаксис, и 25 будет проигнорировано. См. этот вопрос для получения дополнительной информации.

person RichieHindle    schedule 08.04.2010
comment
Плохо, я менял их местами в своем коде и на самом деле один закомментировал... и это работает, я вижу, что я там сделал... Большое спасибо. - person Nicholas; 08.04.2010
comment
Дополнительный вызов деструктора для тела, вероятно, приведет к двойному удалению суставов и кожи - я удивлен, что это не сработало. - person Mark B; 08.04.2010
comment
@RichieHindle - body->~Body(); delete body не просто странно, но может вызвать неопределенное поведение в его примере. Деструктор будет вызываться дважды, и, поскольку его код не устанавливает joints или skin в 0, код выполнит двойное удаление, что может вызвать UB. - person R Samuel Klatchko; 08.04.2010
comment
Как я уже сказал, в моем реальном коде на самом деле нет обоих, только одного или другого, я просто выполнял некоторую отладку. - person Nicholas; 08.04.2010

Настоящие программисты могут писать FortranJava на любом языке! Java требует, чтобы вы выделяли (практически) все динамически, а C++ — нет.

Поскольку никто другой не указал на это (по крайней мере, прямо), в display, похоже, вообще нет причин использовать динамическое размещение. Просто сделайте что-то вроде:

void display(void){
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    Body body;
    body.readSkel("C:\\skel2.skel");
    body.drawBody();

    Body body2;
    body2.readSkel("C:\\skel1.skel");
    body2.drawBody();
    glutSwapBuffers();  
}

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

Точно так же в вашем определении тела вы, похоже, не делаете ничего, что требует динамического распределения.

class Body { 
    std::string skelFile;
    std::string skinFile;
    int totalJoints;
    Skin skin;
    Joint joints[25];
public:
    Body() : totalJoints(0) {}
};

или еще лучше:

class Body { 
    std::string skelFile;
    std::string skinFile;
    Skin skin;
    std::vector<Joint> joints;
public:
   // presumably other stuff goes here...but you don't need a ctor or dtor.
};

Это избавляет от большинства шансов на утечку чего-либо (по крайней мере, в этих частях кода - поскольку мы не видели ваши классы Skin или Joint, трудно предположить, что они могут делать...

person Jerry Coffin    schedule 08.04.2010
comment
Ага, отличный совет. Не используйте динамическое размещение, если в этом нет необходимости. - person jalf; 09.04.2010

Было бы полезно, если бы вы вставили небольшой код, но я бы:

Дважды проверьте свой синтаксис: int *foo = new int[size]; удалить[]фу;

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

person Asklepius M.D.    schedule 08.04.2010
comment
Код указан ниже. Я убедился, что для каждого нового у меня есть удаление. - person Nicholas; 08.04.2010
comment
возможно, сегодня утром я не выпил достаточно кофе, но я вижу два новых оператора Body(), выделяющих память, но только один вызов деструктора. Что происходит с вашим первым Телом? - person Asklepius M.D.; 08.04.2010

Йохен Калмбах — ваш друг.

person Agnel Kurian    schedule 08.04.2010