Почему удалить класс без виртуального dtor - это нормально, если он наследует класс с виртуальным dtor - PullRequest
3 голосов
/ 20 сентября 2019

Рассмотрим код ниже

#include <cstdio>
#include <memory>

struct Base1
{
    Base1() = default;
    virtual ~Base1() = default;
    //~Base1() = default;
};

struct Base2 : public Base1
{
    Base2()
    {
        printf("%s:%d:%s\n", __FILE__, __LINE__, __func__);
    }
    ~Base2() // non-virtual destructor
    {
        printf("%s:%d:%s\n", __FILE__, __LINE__, __func__);
    }
};

struct Derive : public Base2
{
    Derive()
    {
        printf("%s:%d:%s\n", __FILE__, __LINE__, __func__);
    }
    ~Derive()
    {
        printf("%s:%d:%s\n", __FILE__, __LINE__, __func__);
    }
};

int main()
{
    std::unique_ptr<Base2> d = std::make_unique<Derive>();
    return 0;
}

В классе Base2 нет виртуального деструктора, но он наследует Base1 с виртуальным деструктором.

Код std::unique_ptr<Base2> d = std::make_unique<Derive>(); пытаетсячтобы удалить Base2 на объекте типа Derive, я ожидал, что вызывается только Base2, но не для Derive.

Но на самом деле все работает нормально:

main.cpp:15:Base2
main.cpp:27:Derive
main.cpp:31:~Derive
main.cpp:19:~Base2

Так что, если у «реального» базового класса (здесь Base1) есть виртуальный dtor, то все унаследованные классы не обязательно должны иметь виртуальный dtor, не так ли?Если да, где я могу найти соответствующий документ об этом?

Ответы [ 2 ]

6 голосов
/ 20 сентября 2019

Да, если у класса есть базовый класс, чей деструктор объявлен как virtual, его деструктор также будет virtual;независимо от того, объявлено оно явно как virtual или нет.

Даже если деструкторы не наследуются, если базовый класс объявляет свой виртуальный деструктор, производный деструктор всегда переопределяет его.Это позволяет удалять динамически размещенные объекты полиморфного типа посредством указателей на базу.

class Base {
 public:
    virtual ~Base() { /* releases Base's resources */ }
};

class Derived : public Base {
    ~Derived() { /* releases Derived's resources */ }
};

int main()
{
    Base* b = new Derived;
    delete b; // Makes a virtual function call to Base::~Base()
              // since it is virtual, it calls Derived::~Derived() which can
              // release resources of the derived class, and then calls
              // Base::~Base() following the usual order of destruction
}

Из стандарта [class.dtor] / 13

(выделено мной)

Предполагаемый деструктор может быть объявлен виртуальным ([class.virtual]) или чисто виртуальным ([class.abstract]).Если деструктор класса является виртуальным и любые объекты этого класса или любого производного класса создаются в программе, деструктор должен быть определен. Если класс имеет базовый класс с виртуальным деструктором, его деструктор (объявленный пользователем или неявно) является виртуальным .

6 голосов
/ 20 сентября 2019

Потому что если он наследует класс с виртуальным деструктором, то у него есть виртуальный деструктор, даже если вам не нужно явно отмечать его как virtual.

~Base2() // actually a virtual destructor

Обратите внимание, есливы хотите убедиться, что ~Base2() является виртуальным независимо от ~Base1(), тогда вы должны пометить его как virtual.Но обычно вам нужно убедиться, что Base1() действительно виртуальный.Это может быть достигнуто с помощью спецификатора override:

~Base2() override; // fail to compile if ~Base1() is not virtual
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...