Работает ли dynamic_cast внутри перегруженного оператора delete? - PullRequest
5 голосов
/ 22 июня 2011

Я сталкивался с этим:

struct Base {
  void* operator new (size_t);
  void operator delete (void*);
  virtual ~Base () {}  // <--- polymorphic
};
struct Derived : Base {};

void Base::operator delete (void *p)
{
  Base *pB = static_cast<Base*>(p);
  if(dynamic_cast<Derived*>(pB) != 0)
  { /* ... NOT reaching here ? ... */ }
  free(p);
}

Теперь, если мы это сделаем,

Base *p = new Derived;
delete p;

Удивительно, но условие внутри Base :: delete не выполняется Я делаю что-то не так?Или приведение с void* теряет информацию Derived*?

Ответы [ 3 ]

15 голосов
/ 22 июня 2011

Функция operator delete является функцией освобождения необработанной памяти .Он вызывается, когда реальный объект (который раньше находился в этой памяти) уже разрушен.Т.е. к моменту попадания в operator delete ваш объект уже был стерт с лица земли.Память, на которую указывает указатель, по сути «сырая», она больше не содержит объект.Попытка использовать любую полиморфную функциональность в этой необработанной памяти бесполезна - она ​​не будет работать.

В более формальных терминах, согласно стандарту языка, время жизни объекта с нетривиальным деструктором заканчивается, как только начинается его деструктор.В вашем случае все деструкторы уже сделали свою работу.Время жизни объекта уже истекло, в то время как для dynamic_cast требуется «живой» объект.

PS Формально, разрешено использовать dynamic_cast в деструкторе, пока выполняются некоторые условия (см. 12.7/ 5), но когда все деструкторы завершены (как в вашем случае), dynamic_cast больше не может использоваться.

9 голосов
/ 22 июня 2011

Как только ваша перегрузка operator delete получает указатель, указанный объект уничтожен (деструктор ~Derived() уже был вызван).

Вы не можете больше воспринимать его как объект Base или Derived после его уничтожения, поскольку он больше не является объектом Base или Derived.

2 голосов
/ 22 июня 2011

Как уже упоминалось в двух других ответах, тип объекта изменяется по мере выполнения деструкторов.Как только деструктор завершает работу, объект этого типа больше не существует, и существуют только его базовые подобъекты (пока не завершены их деструкторы).

Причиной этого ответа является предложение интересного эксперимента.этот код будет?(О, хорошо, все три ответа уже сказали вам, но эксперимент интересен сам по себе):

#include <iostream>
struct base {
    static void print_type( base const & b ) {   // [1]
        std::cout << b.type() << std::endl;
    }
    virtual std::string type() const {           // [2]
        return "base";
    }
    virtual ~base() { print_type( *this ); }
    base() {          print_type( *this ); }
};
struct derived : base {
    std::string type() const {
        return "derived";
    }
    ~derived() {      print_type( *this ); }
    derived()  {      print_type( *this ); }
};
struct most_derived : derived {
    std::string type() const {
        return "most_derived";
    }
    ~most_derived() { print_type( *this ); }
    most_derived()  { print_type( *this ); }
};
int main() {
    most_derived md;
    base::print_type( md );
}

Примечания:

Для дополнительного удовольствия добавлены также звонки на print_typeв конструкторе.Функция служит для проверки динамического типа объекта в данный конкретный момент времени.Функция print_type (это может быть автономная функция, реализованная в другом модуле перевода - чтобы избежать того, что компилятор увидит внутри нее).При компиляции функции компилятор не может знать, вызывается ли она из конструктора, деструктора или вне их, поэтому сгенерированный код должен использовать механизм динамической диспетчеризации и будет отправлен в окончательное переопределение в каждый момент времени.

В отношении действительности кода гарантируется §12.7 / 2:

Для явного или неявного преобразованияуказатель (значение l), ссылающийся на объект класса X на указатель (ссылку) на прямой или косвенный базовый класс B из X, конструкцию X и построение всех его прямых или косвенных баз, которые прямо или косвенно происходят изB должен начаться, и уничтожение этих классов не должно быть завершено, в противном случае преобразование приводит к неопределенному поведению.Чтобы сформировать указатель на (или получить доступ к значению) прямого нестатического члена объекта obj, должно начаться построение obj, а его уничтожение не должно быть завершено, в противном случае вычисление значения указателя (или получение доступа к значению элемента)приводит к неопределенному поведению.

Преобразования в base& при вызове print_type действительны, так как они выполняются после построения каждого объекта и доуничтожение каждого объекта завершено ( каждый относится к каждому из подобъектов most_derived в программе).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...