Вопросы о выделении и удалении памяти в C ++ - PullRequest
1 голос
/ 26 октября 2009

Я получаю плохую ошибку. Когда я вызываю delete для объекта в верхней части иерархии объектов (в надежде на причину удаления его дочерних объектов), моя программа завершает работу, и я получаю следующее:

*** glibc detected *** /home/mossen/workspace/abbot/Debug/abbot: double free or corruption (out): 0xb7ec2158 ***

, за которым следует что-то вроде дампа памяти. Я искал эту ошибку, и, судя по всему, она возникает, когда вы пытаетесь удалить уже удаленную память. Это невозможно, так как в моем коде есть только одно место, которое пытается это удалить. Вот дурацкая часть: это не происходит в режиме отладки. Код в вопросе:


Terrain::~Terrain()
{
    if (heightmap != NULL) // 'heightmap' is a Heightmap*
    {
        cout << "heightmap& == " << heightmap << endl;
        delete heightmap;
    }
}

Я закомментировал все в деструкторе карты высот, и все еще эта ошибка. Когда возникает ошибка,

heightmap& == 0xb7ec2158

печатается. В режиме отладки я могу медленно пройти по коду и

heightmap& == 0x00000000

напечатано, и нет ошибки. Если я закомментирую «удалить карту высот»; линия, ошибка никогда не возникает. Вышеуказанный деструктор вызывается из другого деструктора (отдельных классов, никаких виртуальных деструкторов или чего-то в этом роде). Указатель карты высот является новым в таком методе:


Heightmap* HeightmapLoader::load() // a static method
{
   // ....
   Heightmap* heightmap = new Heightmap();
   // ....other code
   return heightmap;
}

Может быть, это связано с возвратом указателя, инициализированного в пространстве стека статического метода? Я делаю удаление правильно? Любые другие советы о том, что я мог бы проверить или сделать лучше?

Ответы [ 4 ]

2 голосов
/ 26 октября 2009

В режиме отладки указатели часто устанавливаются в NULL, а блоки памяти обнуляются. Вот почему вы испытываете другое поведение в режиме отладки / выпуска.

Я бы предложил вам использовать умный указатель вместо традиционного указателя

auto_ptr<Heightmap> HeightmapLoader::load() // a static method
{
   // ....
   auto_ptr<Heightmap> heightmap( new Heightmap() );
   // ....other code
   return heightmap;
}

таким образом вам не нужно удалять его позже, поскольку это будет сделано автоматически

см. Также boost :: shared_ptr

2 голосов
/ 26 октября 2009

Что произойдет, если load() никогда не вызывается? Ваш конструктор класса инициализирует heightmap или неинициализируется, когда попадает в деструктор?

Также вы говорите:

... удалить память, которая уже была удалена. Невозможно, так как в моем коде есть только одно место, которое пытается это удалить.

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

1 голос
/ 26 октября 2009

Вполне возможно, что вы звоните этому дтору дважды; в режиме отладки указатель обнуляется при удалении, в оптимизированном режиме он остается один. Хотя это и не совсем правильное решение, первое, что приходит на ум, - это установить heightmap = NULL; сразу после удаления - это не должно быть необходимым, но, безусловно, не повредит, пока вы ищете объяснение, почему вы уничтожаете несколько экземпляров Terrain дважды! -) [[в крошечном количестве кода, который вы показываете, нет абсолютно ничего, что могло бы помочь нам объяснить причину двойного уничтожения.]]

0 голосов
/ 26 октября 2009

Это похоже на классический случай неинициализированного указателя. Как сказал @Greg, что если load () не вызывается из Terrain? Я думаю, вы не инициализируете указатель HeightMap* внутри конструктора Terrain. В режиме отладки этот указатель может быть установлен в NULL, и C ++ гарантирует, что удаление указателя NULL является допустимой операцией и, следовательно, код не падает. Однако в режиме выпуска из-за оптимизаций указатель неинициализируется, и вы пытаетесь освободить какой-то случайный блок памяти, и происходит указанное выше сбой.

...