C ++ чисто виртуальный метод управления памятью вопрос - PullRequest
3 голосов
/ 10 апреля 2011

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

Valgrind говорит, что у меня есть 3 alloc и 2 освобождения.Честно говоря, я только явно вижу 1 alloc и 1 free, но я приму, что есть некоторые, которые я не вижу (пока, но, пожалуйста, кто-нибудь объяснит).Но почему они не сбалансированы по крайней мере?Каждый производный экземпляр также имеет свой собственный 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)

Ответы [ 3 ]

5 голосов
/ 10 апреля 2011

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

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

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

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

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

5 голосов
/ 10 апреля 2011

Когда вы создаете, а затем 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 ++многие современные функции библиотеки, включая строковые объекты и умные указатели.

2 голосов
/ 10 апреля 2011

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

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

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

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

Третий alloc (и второй free) - этоновый и удалить CClass

...