Новый оператор размещения в C ++ сбивает с толку Valgrind Memcheck - PullRequest
2 голосов
/ 28 марта 2020

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

Поэтому я инициализирую их так:

memoryPool = new Object[MAX_OBJECTS];

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

Поэтому существует переменная objectCount, objectCount была инициализирована с действительный инициализатор. Когда пришло время увеличить переменную objectCount

 assert(newObjectCount >= objectCount);
 for (int i = objectCount; i < newObjectCount; i++) {
            new(&(memoryPool[i])) Object(/*Calls the valid initializer of the class*/);
  }
 objectCount = newObjectCount;

Этот метод, который использует новое размещение, я нашел здесь .

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

Может быть, есть лучший способ сделать это, но я не знаю ни одного, и я все уши.

Теперь перейдем к актуальному вопросу. Этот код при запуске через valgrind с проверкой на утечку = full генерирует МНОЖЕСТВО вывода примерно Conditional jump or move depends on uninitialized value(s), и он всегда находится на одном из Object объектов. Будет ли такое распределение «забавных вещей с указателями», которые могли бы испортить Memcheck?

Ответы [ 2 ]

1 голос
/ 31 марта 2020

Также обратите внимание, что если вы действительно хотите написать свой собственный распределитель пулов, вам нужно сообщить Valgrind об операциях с памятью в пуле. Valgrind уже распознает встроенные функции, такие как mallo c, operator new и c.

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

Если вы не настроите свой пул, то Valgrind не обнаружит ошибки, такие как следующий сценарий

  • блок М памяти выделен с new и формирует пул памяти. Valgrind помечает M как выделенный, но не инициализированный.
  • блок S памяти выделен из пула. Valgrind ничего не знает об этой операции.
  • S правильно инициализирован. Valgrind помечает S как инициализированный.
  • Вы делаете некоторые вещи с S
  • S возвращается в пул. Valgrind снова ничего не знает об этом, в то время как он должен пометить S как свободный
  • , блок S снова выделен из пула. Valgrind должен пометить S как выделенный, но не инициализированный, но вместо этого S по-прежнему помечен как инициализированный
  • , теперь вы выполняете «неинициализированное чтение» на S, но Valgrind считает, что S инициализирован, поэтому не выдает ошибку. Теперь у вас есть ложный минус: - (
1 голос
/ 28 марта 2020

Первая проблема заключается в том, что в вашем случае memoryPool[i] уже содержит действительный объект, который перезаписывается вашим новым местоположением без должного удаления старого объекта.

Таким образом, несколько лучший способ был бы:

memoryPool[i].~Object();  // first destroy the obl object
new(&(memoryPool[i])) Object (...);  

Но использование новых мест размещения должно быть исключительным. Если это просто замена объектов, вы не должны использовать такую ​​деликатную технику, а просто использовать простое назначение:

memoryPool[i] = Object (...); // just replace the old object with a new one.  

Конечно, в обоих случаях вы должны соблюдать правило 3 . Если вы этого не сделаете, в зависимости от членов Object, вы все равно можете потерять память.

Наконец, еще одна проблема: поскольку вы выделяете динамический массив c, вам необходимо удалить также массив , когда он вам больше не нужен:

delete[] memoryPool;   // note the []

A гораздо более гибкий и надежный подход будет использовать vector<Object>, который позволит memoryPool динамически расти и уменьшаться, освобождая вас от обязанности управления памятью. Кроме того, вы можете создавать и добавлять элементы по мере необходимости с помощью emplace_back()

...