2009-12-20 6 views
2

У меня возник вопрос о поведении деструктора C++, больше из любопытства, чем что-либо еще. У меня есть следующие классы:Поведение деструктора C++

Base.h

class BaseB; 

class BaseA 
{ 
    public: 
     virtual int MethodA(BaseB *param1) = 0; 
}; 

class BaseB 
{ 
}; 

Imp.h

#include "Base.h" 
#include <string> 

class BImp; 

class AImp : public BaseA 
{ 
    public: 
     AImp(); 
     virtual ~AImp(); 

    private: 
     AImp(const AImp&); 
     AImp& operator= (const AImp&); 

    public: 
     int MethodA(BaseB *param1) { return MethodA(reinterpret_cast<BImp *>(param1)); } 

    private: 
     int MethodA(BImp *param1); 
}; 

class BImp : public BaseB 
{ 
    public: 
     BImp(std::string data1, std::string data2) : m_data1(data1), m_data2(data2) { } 
     ~BImp(); 
     std::string m_data1; 
     std::string m_data2; 

    private: 
     BImp(); 
     BImp(const BImp&); 
     BImp& operator= (const BImp&); 
}; 

Теперь проблема в том, что с помощью этого кода, все работает безупречно. Однако, когда я делаю деструктор для BImp виртуальным, при вызове AImp :: MethodA класс BImp, похоже, не имеет инициализации своих данных (m_data1 и m_data2). Я проверил и убедился, что содержащиеся данные верны во время строительства, поэтому мне было интересно, какая причина этого может быть ...

Cheers!

Редактировать: param1 фактически был ссылкой на B в MethodA. Похоже, я слишком сильно дезинфицировал свой настоящий код!

Редактировать 2: немного измените код, чтобы показать два разных файла. Протестировано, что этот код компилируется, колодец. Извини за это!

+0

Ну какой тип вы проходите MethodA? Если вы передаете экземпляр B в MethodA (который передает его в BImp), вы очень непослушны. – Skurmedel

+2

Вы разрезаете (передаете значение B по значению) и используете reinterpret_cast, когда вам этого не нужно. – avakar

+0

Еще одна странность в том, что вы проходите MethodA без указателя, который вы делаете повторно накладываете ... этот код злой! :) – Skurmedel

ответ

9

Если вы бросаете между родственными видами, как вы в этом случае, you should use static_cast or dynamic_cast, а не reinterpret_cast, потому что компилятор может изменить значение указателя объекта во время его отливки к более производного типа. Результат reinterpret_cast в этом случае не определен, поскольку он просто берет значение указателя и делает вид, что это другой объект, не обращая внимания на макет объекта.

+0

В моем коде была опечатка от чрезмерной дезинфекции. Фактический MethodA принимает параметры по ссылке. – sohum

+0

Отредактировано. ... (требуется не менее 15 символов) –

+0

Хм ... дайте мне попробовать. Таким образом, поведение виртуального деструктора является чисто случайным? – sohum

2

MethodA принимает свои параметры по значению. Это означает, что копия передается (и копия должна быть уничтожена). Это мое лучшее предположение, почему у вас может быть уничтожен BImpl, которого вы не ожидали, но я не понимаю, что может с ним иметь виртуальный или не виртуальный характер деструктора A.

Но этот код не может скомпилировать - вы используете класс B при объявлении виртуальной функции в A, но B не определен до более позднего времени. И я не знаю, что происходит с этим приведением - вы не можете использовать типы типов reinterpret_cast. Возможно, если вы разработаете тестовый пример, демонстрирующий вашу проблему, и опубликуйте это?

1

В этом коде много лишнего материала, поэтому я поражен тем, что он работает или компилируется в любом случае.

  • Передача параметров по значению вместо ссылки на MethodA
  • Кастинг B к BImp через reinterpret_cast - плохая идея! Если вы собираетесь бросить в этом направлении, то dynamic_cast является самым безопасным.
  • Я не вижу, как вы должны получить BImp из B. Вы не вызываете каких-либо конструкторов, и у вас нет ни одного, который мог бы быть вызван, чтобы принять бит. Ваш конструктор по умолчанию для BImp является закрытым, и присвоение B, у которого нет данных, отлитых от BImp, которые все еще не имеют данных, для BImp, все равно не даст вам никаких данных!
0

Ваш код плохо сформирован. Недействительно C++.В языке C++ reinterpret_cast может использоваться только для перевода между типами указателей, ссылочными типами, для преобразования конверторов в целые числа (в любом направлении).

В вашем коде вы пытаетесь использовать reinterpret_cast для преобразования из типа B в тип BImp. Это явно запрещено в C++. Если ваш компилятор разрешает этот код, вы должны проконсультироваться с документацией своего компилятора, чтобы определить, что происходит.

Другие ответы уже упомянуты «нарезка». Имейте в виду, что это не что иное, как просто предположение о нестандартном поведении вашего конкретного компилятора. Это не имеет никакого отношения к языку C++.

1

Несколько комментариев:

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

  • МетодA Принимает указатель BaseB как параметр только для того, чтобы указатель, переинтерпретированный как BImp (производный класс BaseB), является опасным. Нет гарантии, что в MethodA передается что-то другое, кроме BImp. Что произойдет, если только объект BaseB будет MethodA? Потенциально много плохих вещей, я бы заподозрил.

  • Я предполагаю, что ваш код «работает безупречно», потому что вы передаете только BImp в MethodA. Если вы только передаете BImp в MethodA, тогда сделайте подпись совпадающей с намерением (это имеет дополнительное преимущество для удаления этого ужасного вызова переинтерпрета).

Смежные вопросы