Dynami c приведен в деструктор - PullRequest
8 голосов
/ 29 января 2020

Законен ли этот код?

class Base1 {
};

class Base2 {
public:
    virtual ~Base2() {
        if (!dynamic_cast<Base1*>(this))
            std::cout << "aaaa" << std::endl;
    }
    Base2() {
    }
};

class MyClass: public Base1, public Base2 {
public:
    MyClass() {
    }
    virtual ~MyClass() {
        std::cout << "bbb" << std::endl;
    }
};

int main() {
    MyClass s;
    return 0;
}

Я вижу оба отпечатка, но должен видеть только один. Я думаю, что динамический c составлен неверно. Можно ли сделать такую ​​проверку?

Ответы [ 3 ]

7 голосов
/ 29 января 2020

Может быть, я сам нашел решение, ответ - нет, это невозможно:

Из Bullet 6 документации cppreference.com :

Когда dynamic_cast используется в конструкторе или деструкторе (прямо или косвенно), а выражение относится к объекту, который в настоящее время находится в процессе строительства / уничтожения, объект считается наиболее производным объектом. Если new-type не является указателем или ссылкой на собственный класс конструктора / деструктора или одну из его баз, поведение не определено.

См. Также [class.cdtor] / 6 стандарта.

Поскольку я выполняю приведение к Base1 в деструкторе Base2, это поведение не определено.

3 голосов
/ 30 января 2020

Я согласен с ответом @ j6t, но вот расширенные рассуждения со стандартными ссылками.

Особое поведение dynamic_cast для строящихся и разрушаемых объектов описывается [class.cdtor] / 5 стандарта C ++ 17 (окончательный вариант) и аналогично предыдущим версиям стандарта.

В частности, в нем говорится:

Когда используется dynamic_­cast [...] в деструкторе, [...], если операнд dynamic_­cast относится к строящемуся или разрушаемому объекту, этот объект считается наиболее производным объектом, который имеет тип [. ..] класс деструктора. Если операнд dynamic_­cast относится к объекту, который [...] уничтожается, и тип операнда stati c не является указателем или объектом собственного класса деструктора [...] или одного из его Основы, dynamic_cast приводит к неопределенному поведению.

Неопределенное поведение здесь не применимо, так как операндом является выражение this, которое тривиально имеет тип указателя на собственный класс деструктора, так как оно появляется в самом деструкторе.

Однако в первом предложении говорится, что dynamic_cast будет вести себя так, как если бы *this был наиболее производным объектом типа Base2, и поэтому приведение к Base1 может никогда не удастся, потому что Base2 не является производным от Base1, а dynamic_cast<Base1*>(this) всегда будет возвращать нулевой указатель, что приведет к поведению, которое вы видите.


cppreference.com заявляет, что неопределенное поведение происходит, если тип назначения приведения не является типом класса деструктора или одной из его баз, вместо того, чтобы иметь это приложение к типу операндов. Я думаю, что это просто ошибка. Вероятно, упоминание « new-type » в пуле 6 должно было сказать « выражение », что соответствовало бы моей интерпретации выше.

2 голосов
/ 29 января 2020

dynamic_cast четко определен в этой ситуации. Это верно, что вы наблюдаете обе строки вывода.

Вы ошибаетесь, полагая, что в деструкторе Base2 this это производный класс. В настоящее время часть производного класса уже уничтожена, поэтому она больше не может быть производным классом. Фактически, во время работы деструктора Base2 объект, на который указывает this , является только объектом Base2. Поскольку Base2 никак не связан с Base1, dynamic_cast возвращает нулевой указатель, и условное обозначение вводится соответствующим образом.

Редактировать: Стандарт гласит :

Когда dynamic_­cast используется в конструкторе [...] или в деструкторе [...], если операнд dynamic_­cast относится к строящемуся или разрушаемому объекту, этот объект считается наиболее производным объектом, который имеет тип конструктора или класса деструктора. Если операнд dynamic_­cast относится к строящемуся или разрушаемому объекту, а тип операнда stati c не является указателем или объектом собственного класса конструктора или деструктора или одной из его основ, то dynamic_­cast приводит к неопределенному поведению.

Операнд this относится к разрушаемому объекту. Следовательно, класс деструктора (Base2) считается наиболее производным классом, и именно поэтому объект никак не связан с типом назначения (Base1*). Кроме того, тип операнда this stati c равен Base2* const, что явно указывает на собственный класс деструктора. Следовательно, правило о неопределенном поведении не применяется. Таким образом, мы имеем четко определенное поведение.

...