Есть некоторый класс, допустим X, и он определен в некой dll. И что самое главное для нас, у него есть ВИРТУАЛЬНЫЙ ДЕСТРУКТОР.
Назовем этом модуль object.dll
class X
{
...
public:
X ();
virtual ~X ();
};
Есть также другой dll, в котором есть функция, выполняющая динимическое создание экземпляра объекта класса X. Она конечно может делать с ним ещё что-то, но это сейчас не важно.
А этот модуль назовем creator.dll. Связь с object.dll на этапе загрузки.
X* CreateObject ()
{
X * pX = new X;
// Do something
return pX;
}
И наконец имеется третий модуль ( может как exe, так и dll).
Назовем его main.exe. Связь с object.dll — статическая (на этапе загрузки), а с creator.dll — динамическая (через LoadLibrary )
И вот имеется некоторый код в main.exe :
// Что делаем с pX
// ПРИ ПОПЫТКЕ ВЫЗВАТЬ ЛЮБУЮ ВИРТУАЛЬНУЮ ФУНКЦИЮ ИЛИ
// УДАЛИТЬ ОБЪЕКТ через delete pX ВСЁ ВАЛИТСЯ.
}
Причина состоит в следующем.
Если у класса есть ВИРТУАЛЬНЫЙ ДЕСТРУКТОР, то в модуле где происходит создание этого объекта создается виртуальная таблица, в которую заносятся нормальные адреса функций. Но при этом после создания объекта ( после вызова new для выделения памяти и после вызова кода создания объекта ( вызов конструкторов базовых классов, инициализации указателя на таблицу виртуальных функций (используется таблица, определенная в модуле, где объявлен это класс), и вызова нашего конструктора), т.е. когда объект полность готов выполняется следующее и очень непонятное действие. Указатель на таблицу виртуальных функций инициализируется адресом таблицы из модуля, где СОЗДАН объект. При этом, она содержит те же адреса, что таблица из модуля, где объявлен класс.
Таблица виртуальных функций предствляется структуру, которая статически размещается в модуле. Соответсвенно после того, как я выгрузил модуль creator.dll занимаемые им адреса памяти стали недействительные, и мой мой объект содержал указатель на уже несуществующую таблицу виртуальных функций. Естественно, любой вызов виртуальной функции заканчивался крахом.
Ещё непонятно то, что зачем-то в модуле, где создается объект генируется код деструктора, но он не попадает в таблицу виртуальных функций.
При отсутствии виртуального деструктора, но если есть виртуальные функции, то всё работает нормально.
Всё выше сказанное относится к компилятору VC++ (как 12 версии от VC 6.0, так и 13 версии от .NET ). Анализ производился на основе сгенерированного текста на ассемблере.
Данный эффект не проявляется при использовании компилятора Borland C++ 5.5.
Не надо меня спрашивать, зачем мне это понадобилось. Также не надо давать советов, как это обойти (это уже давно решено).
Вопрос сосоит в том, почему так происходит, а именно, зачем создается ещё одна таблица виртуальных функций, хотя она содержит те же адреса.
Может в этом имеется тайный смысл или это просто баг компилятора.
Интересно, понял ли кто-нибудь написаное выше ;)))
R>Вопрос сосоит в том, почему так происходит, а именно, зачем создается ещё одна таблица виртуальных функций, хотя она содержит те же адреса. R>Может в этом имеется тайный смысл или это просто баг компилятора.
Видимо было так удобнее авторам компилятора ... 1 таблица на 1 класс на 1 модуль это в идеале ... а как в жизни ...
в C/C++ Users Journal была статейка и там эта тема поднималась ...
R>Интересно, понял ли кто-нибудь написаное выше ;)))
Не ... но это не мешает мне написать ответ (C) Igor Soukhov