2011-04-09 2 views
3

У меня есть базовый класс (AClass здесь), который имеет protected ресурс (str здесь), которые получают free-х «d в AClass деструктора. Производный BClass имеет чистый виртуальный метод Init. Производный CClass реализует Init, который выделяет некоторую память для защищенного ресурса.C++ чисто виртуальный вопрос управления метод памяти

Valgrind говорит, что у меня есть 3 выделения и 2 освобождения. Честно говоря, я только явно см. 1 alloc и 1 бесплатно, но я соглашусь с тем, что есть некоторые, которых я не вижу (на данный момент, но, пожалуйста, кто-нибудь объяснит). Но почему они не сбалансированный не менее? Имеет ли каждый производный экземпляр свой собственный str, и он не получает free 'd?

#include <stdio.h> 
#include <stdarg.h> 
#include <stdlib.h> 

class AClass; 
class BClass; 
class CClass; 

class AClass 
{ 
public: 
    AClass() : str(NULL) { 
    printf("AClass Constructor with no params.\n"); 
    str = (char *) malloc(5 * sizeof(char)); 
    } 
    AClass(char *foo) { 
    printf("AClass Constructor with params, %s.\n", foo); 
    } 
    virtual ~AClass() { 
    printf("AClass Destructor. Getting ready to free %s\n", str); 
    free(str); 
    printf("\tfree.\n"); 
    } 

protected: 
    char *str; 
}; 

class BClass : public AClass 
{ 
public: 
    BClass() { 
    printf("BClass Constructor with no params.\n"); 
    }; 
    BClass(char *foo) : AClass(foo) { 
    printf("BClass Constructor with params, %s.\n", foo); 
    str = foo; 
    }; 
    virtual void Init() = 0; 
    virtual ~BClass() { 
    printf("BClass Destructor.\n"); 
    }; 
}; 

class CClass : public BClass 
{ 
public: 
    CClass() { 
    printf("CClass Constructor with no params.\n"); 
    }; 
    void Init() { 
    printf("CClass Init method.\n"); 
    str = (char *) malloc(255 * sizeof(char)); 
    printf("\tmalloc.\n"); 
    snprintf(str, 255 * sizeof(char), "Hello, world."); 
    }; 
    virtual ~CClass() { 
    printf("CClass Destructor.\n"); 
    }; 
}; 

int main (int argc, char const *argv[]) 
{ 
    printf("Start.\n"); 
    BClass *x = new CClass(); 
    x->Init(); 
    delete x; 
    printf("End.\n"); 
    return 0; 
} 

Вот выход Valgrind.

==6641== Memcheck, a memory error detector 
==6641== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. 
==6641== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info 
==6641== Command: ./a.out 
==6641== 
Start. 
AClass Constructor with no params. 
BClass Constructor with no params. 
CClass Constructor with no params. 
CClass Init method. 
     malloc. 
CClass Destructor. 
BClass Destructor. 
AClass Destructor. Getting ready to free Hello, world. 
     free. 
End. 
==6641== 
==6641== HEAP SUMMARY: 
==6641==  in use at exit: 5 bytes in 1 blocks 
==6641== total heap usage: 3 allocs, 2 frees, 268 bytes allocated 
==6641== 
==6641== LEAK SUMMARY: 
==6641== definitely lost: 5 bytes in 1 blocks 
==6641== indirectly lost: 0 bytes in 0 blocks 
==6641==  possibly lost: 0 bytes in 0 blocks 
==6641== still reachable: 0 bytes in 0 blocks 
==6641==   suppressed: 0 bytes in 0 blocks 
==6641== Rerun with --leak-check=full to see details of leaked memory 
==6641== 
==6641== For counts of detected and suppressed errors, rerun with: -v 
==6641== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 18 from 7) 
+0

И что происходит, когда вы включаете опцию '--leak-check = full'? –

+1

вид несвязанного, но есть ли причина, по которой вы используете char * вместо std :: string? – MahlerFive

+0

У вас есть 'malloc' в конструкторе' AClass'. Почему вы не делаете 'printf (" \ tmalloc. \ N ")' для этого 'malloc'? Вот почему вы не можете видеть свой несбалансированный 'malloc' в выводе вашей программы. – AnT

ответ

5

Когда вы строите, а затем Init экземпляр вашего CClass, то str указатель первого присваивается указатель из malloc вызова в конструкторе AClass по умолчанию, а затем присваивается указатель из malloc вызова в CClass::Init. Память, выделенная в конструкторе по умолчанию AClass, никогда не освобождается, и указатель теряется, когда str перезаписывается в CClass::Init.

Вы можете проверить значение, отличное от NULL, в указателе str, прежде чем переустанавливать его в CClass::Init. В качестве альтернативы, вы можете инкапсулировать str назначения в функции члена, который выполняет эту проверку, так что эта проблема не возникает в другом месте:

void allocate_str(int size) { 
    if (str) free(str); 
    str = (char*) malloc(size * sizeof(char)); 
} 

еще лучше, вы могли бы использовать в C++ во время выполнения многих современных функций библиотечных , включая строковые объекты и интеллектуальные указатели.

+0

+1 для рекомендации std :: string –

5

Это не имеет ничего общего с виртуальными функциями. Три распределения, которые, valgrind детектирует являются:

  1. new CClass в main.
  2. malloc в AClass конструктор.
  3. malloc в CClass::Init.

Относительно того, почему звонки не сбалансирован: вы протечки str, который alloc'd в AClass конструктор - вы меняете str указатель в CClass::Init:

void Init() { 
    // ... 
    str = (char *) malloc(255 * sizeof(char)); 
    // ... 
}; 

без первого освобождения ранее выделенный буфер.

+0

Благодарим вас за то, что вы выбрали 3 allocs явно. Наверное, я мог подумать, что через дополнительный командный переключатель Valgrind, но я все еще учу, как интерпретировать его вывод. –

2

Конструктор AClass по умолчанию имеет следующую строку:

str = (char *) malloc(5 * sizeof(char)); // hey, 5 bytes! 

В вашем Init, вы его потеряете, когда вы делаете это

str = (char *) malloc(255 * sizeof(char)); 

Третий Alloc (и второй бесплатно) является новым и удалить от CClass