C ++ Деструктор Поведение - PullRequest
       34

C ++ Деструктор Поведение

2 голосов
/ 21 декабря 2009

У меня был вопрос о поведении деструктора 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). Я проверил и удостоверился, что содержащиеся в нем данные верны во время строительства, поэтому мне было интересно, что может быть причиной этого ...

Ура!

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

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

Ответы [ 5 ]

9 голосов
/ 21 декабря 2009

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

2 голосов
/ 21 декабря 2009

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

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

1 голос
/ 21 декабря 2009

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

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

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

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

1 голос
/ 21 декабря 2009

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

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

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

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

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

...