ЭЛТ виртуальный деструктор

Сегодня я столкнулся с повреждением кучи, вызванным различными настройками CRT (MTd MDd) в моей dll и моем реальном проекте.
Что мне показалось странным, так это то, что приложение падало только тогда, когда я устанавливал деструктор в dll как виртуальный.
Есть ли простое объяснение этому? Я понимаю, что не могу освободить память, которой нет в моей куче, но где именно разница, когда я определяю деструктор как не виртуальный.

Какой-то код, чтобы сделать его немного понятнее

DLL

#pragma once
class CTestClass
{
public:
_declspec(dllexport) CTestClass() {};
_declspec(dllexport) virtual ~CTestClass() {};
};

И мой проект

int main(int argc, char* argv[])
{
CTestClass *foo = new CTestClass;
delete foo; // Crashes if the destructor is virtual but works if it's not
}

6

Решение

Есть разница между

class CTestClass
{
public:
_declspec(dllexport) CTestClass() {}
_declspec(dllexport) virtual ~CTestClass() {}
};

а также

__declspec(dllexport) class CTestClass
{
public:
CTestClass() {}
virtual ~CTestClass() {}
};

В первом случае вы указали компилятору экспортировать только две функции-члена: CTestClass :: CTestClass () и CTestClass :: ~ CTestClass (). Но в последнем случае вы бы указали компилятору также экспортировать таблицу виртуальных функций. Эта таблица необходима после того, как вы получили виртуальный деструктор. Так что это может быть причиной аварии. Когда ваша программа пытается вызвать виртуальный деструктор, она ищет его в соответствующей таблице виртуальных функций, но она не инициализируется должным образом, поэтому мы не знаем, на что она тогда указывает. Если ваш деструктор не виртуальный, то вам не нужна таблица виртуальных функций, и все работает просто отлично.

2

Другие решения

Вы действительно не опубликовали достаточно кода, чтобы быть уверенным. Но ваш пример НЕ должен терпеть крах, потому что в этом нет ничего плохого:

int main(int argc, char* argv[])
{
// 1. Allocated an instance of this class in *this/exe* heap, not the DLL's heap
// if the constructor allocates memory it will be allocated from the DLL's heap
CTestClass *foo = new CTestClass;

// 2. Call the destructor, if it calls delete on anything it will be freed from the DLL's heap since thats where the destructor is executing from. Finally we free the foo object from *this/exe* heap - no problems at all.
delete foo;
}

Я подозреваю, что в вашем реальном коде вы должны использовать оператор delete для объекта, оператор new был выполнен в контексте библиотеки DLL. А без виртуального ключевого слова вы, скорее всего, пропустите вызов деструктора, который выполняет кросс-контекстное удаление.

0

Виртуальный деструктор необходим только при наличии некоторого дерева иерархии наследования. Ключевое слово Virtual гарантирует, что указатель на фактический объект (не на тип объекта) будет уничтожен путем нахождения его деструктора в Vtable. Поскольку в этом примере, следуя приведенному вами коду, CTestClass не наследуется ни от какого другого класса, он в некотором смысле является базовым классом и, следовательно, не нуждается в виртуальном деструкторе. Я предполагаю, что, возможно, есть другое правило, вызывающее это, но вы не должны использовать виртуальные с базовыми классами. Каждый раз, когда вы создаете производный объект, вы также создаете его базу (по полиморфной причине), и база всегда уничтожается (производная уничтожается, только если вы сделаете деструктор для него виртуальным, и, следовательно, поместите его в виртуальную таблицу времени выполнения vlookup) ,

Спасибо

0
По вопросам рекламы [email protected]