почему скалярный деструктор удаления вызывается в результате удаления вектора в Windows? - PullRequest
6 голосов
/ 09 февраля 2011

У меня есть код, который течет в Windows.Он отлично работает на многих платформах Unix, и утечка происходит только в Windows.Бинарный файл состоит из exe, 1 dll и 2 статических библиотек.Exe связывается как с DLL, так и со статическими библиотеками, в то время как статические библиотеки также связываются с DLL.Утечка происходит в исполняемом коде, когда вместо вызова деструктора удаления вектора по какой-то причине вызывается скалярный деструктор удаления.Это приводит к удалению только первого объекта в массиве, в то время как остальная часть массива остается в памяти.

Вытекающий псевдокод выглядит следующим образом:

class MyClassFromExe : public MyBaseClassFromDll {
  public:
    ClassFromDll* m_arr;

    MyClassFromExe(unsigned int size)  
    {
      m_arr = new ClassFromDll[size];
    }

    ~MyClassFromExe() 
    {
      delete [] m_arr;
    }
};

void func()
{
  MyClassFromExe obj(3);
}

When func () завершается и вызывается деструктор. Я вижу, что вызывается только деструктор первого объекта в m_arr.Из отладчика я вижу, что это делается из скалярного деструктора удаления, а не из деструктора удаления вектора.Это объясняет, почему уничтожен только первый объект.Что мне нужно понять, так это то, почему скалярный деструктор удаления вызывается при использовании delete [] ???

Я нашел этот поток - Почему деструктор удаления вектора вызывается в результате скалярного удаления?.Я следовал предложениям и убедился, что все модули скомпилированы с /MD.

Важно отметить, что когда dll, которая содержит ClassFromDll, была статической библиотекой, а не dll, все работало нормально.Утечка началась только тогда, когда статическая библиотека была изменена на dll.В то время как программа просачивается в режим Release, она вылетает в режиме Debug при delete [] m_arr.Сбой происходит в строке 52 dbgdel.cpp - _BLOCK_TYPE_IS_VALID (pHead-> nBlockUse).

На платформах Unix эта библиотека также является общей библиотекой, и, как ожидается, там вызывается деструктор удаления векторов, и утечки нет.Может ли быть проблема с компилятором VC?Или, может быть, нужно изменить некоторые другие настройки проектов?Я пользуюсь VC2003.

Заранее спасибо!

Ответы [ 5 ]

4 голосов
/ 17 февраля 2011

Это старая проблема в VC ++, касающаяся библиотек DLL и объектных массивов. Причина - неправильная оптимизация компилятора, как объясняет Microsoft.

http://support.microsoft.com/kb/121216/en-us

Лучше использовать STL-контейнеры, у которых нет проблем из-за использования распределителя.

1 голос
/ 17 февраля 2011

Класс в DLL чрезвычайно обидчив, без особой помощи со стороны компиляторов. За подробностями обращайтесь к этому ответу. ПОЧЕМУ это проблематично: Как я могу вызвать функцию DLL C ++, которая принимает параметр типа stringstream из C #? .

Краткая версия: если интерфейс класса использует ЛЮБОЙ встроенный код, вы точно так же столкнетесь с потенциальными проблемами. Включен любой шаблонный объект (например, std::string).

Наверное, поэтому. Это похоже на проблему, которую предлагает Микаэль Перссон.

0 голосов
/ 17 февраля 2011

В общем, я бы рекомендовал использовать std :: vector вместо ClassFromDLL *. Построить его, передав размер . Удаление будет автоматически. К сожалению, у меня нет большого опыта работы с delete [], потому что я всегда позволяю стандартной библиотеке сделать это для меня;

0 голосов
/ 17 февраля 2011

Посмотрев на код, я бы предположил, что объект был скопирован ctor по умолчанию, что приводит к ошибке двойного удаления.Это неопределенное поведение.Может показаться, что он работает, но не работает из-за, казалось бы, не связанных изменений, таких как переключение с LIB на DLL.

0 голосов
/ 09 февраля 2011

Я думаю, что это явный случай размещения в одной куче и удаления в другой (помните, что delete [] должен запросить кучу для количества элементов в массиве, и если куча даже не содержит этот указатель, он вернет «ошибку» (не совсем), и будет предполагаться, что это всего лишь один элемент и вместо него будет использоваться скалярное удаление).Я думаю, что ваша проблема была потеряна при попытке свести ее к простому примеру кода.Я бы посоветовал вам прочитать эту статью (она старая, но техника удаления все еще очень актуальна, я сама использую вариант этой техники, и она работает как шарм).Одним из современных способов сделать это является присоединение указателя функции удаления к интеллектуальному указателю (shared_ptr), который обрабатывает ваш объект, таким образом, назначая этот указатель функции удаления одновременно с созданием объекта в заводской функции, вы гарантируетечто удаление будет вызвано в той же куче, из которой оно было выделено.

...