Можно ли пометить сегмент памяти как «за пределами», чтобы менеджер кучи не выделял из него? - PullRequest
1 голос
/ 25 ноября 2010

Ранее сегодня я задал этот вопрос .

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

Запуск юнит-теста из отладчика

// Construct object
Object* pObject = new Object(...);
// Pointer value of pObject == 0x05176960

// Lots of other code
// ...

// Destroy object
delete pObject;

// Construct object again
pObject = new Object(...);
// Pointer value of pObject == 0x05560194     /* Different memory location */

Запуск юнит-теста изкомандная строка

// Construct object
Object* pObject = new Object(...);
// Pointer value of pObject == 0x05176960

// Lots of other code
// ...

// Destroy object
delete pObject;

// Construct object again
pObject = new Object(...);
// Pointer value of pObject == 0x05176960     /* Same memory location */

В итоге:

  • При запуске модульного теста из командной строки , последующие вызовы new для выделенияObject (delete предыдущий Object перед выделением нового) всегда возвращает тот же адрес в памяти.
  • При запуске модульного теста из отладчик , последующие вызовы new для выделения Object (delete с предыдущим Object перед выделением нового) всегда возвращают уникальный адрес в памяти.

Проблема заключается в том, что поскольку выделения Object всегда получают один и тот же адрес в памяти при запуске через командную строку, карта, к которой я обращаюсь и в которой хранится указатель old , все еще можетбудет использоваться, и тест не потерпит крах.Но я хочу, чтобы мой модульный тест завершился сбоем, когда исправление дефекта не было установлено, чтобы убедиться, что оно не завершается сбоем и дефект не возвращается.

На мой вопрос есть 2 части:

  1. Почему диспетчер кучи повторно использует ту же часть памяти при запуске модульного теста из командной строки, а не при запуске модульного теста из отладчика?

  2. Есть ли настройка компилятора, которую я мог бы использовать на своем тестовом жгуте, или метод, который я могу вызвать, чтобы предотвратить повторное использование менеджером кучи удаленного раздела памяти, чтобы позволить мне правильнонаписать мой юнит тест? 1


1 Очевидно, что один из способов сделать это - не удалить исходный объект, а часть кода, котораявыделяет это в моем производственном коде, и это может привести к утечкам памяти.

Ответы [ 4 ]

2 голосов
/ 25 ноября 2010

Вы можете заменить new и delete вашими собственными версиями с желаемым поведением.

2 голосов
/ 25 ноября 2010

Ваш юнит-тест имеет недостатки, поскольку он полагается на неопределенное поведение.Вам следует переписать свой модульный тест, чтобы он не зависел от неопределенного поведения, и в этом случае он всегда будет проходить независимо от того, как менеджер памяти решит выделить память.1003 *

Object* pObject = new Object(...);
...
delete pObject;
pObject = new Object(...);
// Use dangling pointer to first object, and if it crashes, the unit test fails
// This is WRONG since a crash isn't guaranteed

Вместо этого следует реструктурировать модульный тест, чтобы он работал следующим образом:

Object* pObject = new Object(...);
...
// Check to see if there are dangling references to pObject right before we
// delete it.  If there are, assert() and fail the unit test.
assert(NoDanglingReferences(pObject));
delete pObject;
// Continue on with more tests
1 голос
/ 25 ноября 2010

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

Вы можете написать собственный менеджер , как пользователь Andreas Brinck предлагает:но что бы это сделать?Он не создает память из воздуха, он запрашивает ее откуда-то вроде кучи CRT или кучи операционной системы.

Сценарий А. Он не вернет память в нижележащую кучу - у вас будет утечка иблок памяти по-прежнему будет отображаться в адресное пространство, и он будет доступен.

Сценарий B. Он вернет память в базовую кучу - тогда, когда ваш менеджер попытается выделить память снова, базовая куча может вернуть этот блок снова,Также вы не знаете, что делает основная куча, когда вы возвращаете ей память.Это может сделать его неподключенным или нет - поэтому доступ к этой памяти может привести к сбою или нет.

Суть в том, что вы облажались.Попытка проверить неопределенное поведение не будет очень продуктивной.

0 голосов
/ 25 ноября 2010

Это пример НЕОПРЕДЕЛЕННОГО ПОВЕДЕНИЯ. Ни C ++, ни менеджер кучи не определяют, как будет выделяться память. Вы не можете полагаться ни на повторное использование памяти, ни на повторное использование. Когда вы делаете что-то, как описано выше, нет способа определить или изменить, будет ли возвращаемый указатель отличаться от первого выделенного.

...