18.4 Virtual destructors, virtual assignment, and overriding virtualization


https://www.learncpp.com/cpp-tutorial/virtual-destructors-virtual-assignment-and-overriding-virtualization/
c++はdefault destructorを提供していますが、自分で作成する必要がある場合があります.
(特にメモリの再割り当てが必要な場合)
継承を処理する場合はdestructorをvirtualにしなければなりません
次の例を示します.
#include <iostream>
class Base
{
public:
    ~Base() // note: not virtual
    {
        std::cout << "Calling ~Base()\n";
    }
};

class Derived: public Base
{
private:
    int* m_array;

public:
    Derived(int length)
      : m_array{ new int[length] }
    {
    }

    ~Derived() // note: not virtual (your compiler may warn you about this)
    {
        std::cout << "Calling ~Derived()\n";
        delete[] m_array;
    }
};

int main()
{
    Derived *derived { new Derived(5) };
    Base *base { derived };

    delete base;

    return 0;
}
main関数では、baseはBase pointerなので、baseが削除されると、Base destructorがvirtualであることがプログラムに表示されます.上のプログラムではBasedestructorはvirtualではないのでBasedestructorのみ呼び出します
しかし、Derived destructorを呼び出す必要があります.
上記の場合、m arrayは無効になりません.
上記の問題はBase'sdestructorをvirtualに設定して解決できます
#include <iostream>
class Base
{
public:
    virtual ~Base() // note: virtual
    {
        std::cout << "Calling ~Base()\n";
    }
};

class Derived: public Base
{
private:
    int* m_array;

public:
    Derived(int length)
      : m_array{ new int[length] }
    {
    }

    virtual ~Derived() // note: virtual
    {
        std::cout << "Calling ~Derived()\n";
        delete[] m_array;
    }
};

int main()
{
    Derived *derived { new Derived(5) };
    Base *base { derived };

    delete base;

    return 0;
}
上記のようにvirtualに設定すると、プログラムは予想通りに実行されます.

Rule


Whenever you are dealing with inheritance, you should make any explicit destructors virtual.
virtualdestructorが必要なら
クラスに特別なインプリメンテーションが必要ない場合は、次のように記述できます.
virtual ~Base() = default; // generate a virtual default destructor
default destructorの機能しか必要ありませんがvirtualが必要な場合は上記のように記述します

Virtual assignment


まずこの話題をスキップします.

Ignoring virtualization


functionのvirtualを無視しようとする人は少ない
次の例を示します.
class Base
{
public:
    virtual ~Base() = default;
    virtual const char* getName() const { return "Base"; }
};

class Derived: public Base
{
public:
    virtual const char* getName() const { return "Derived"; }
};
派生オブジェクトを受信したBaseポインタを使用してBase::getName()を呼び出すことができます.
このとき
#include <iostream>
int main()
{
    Derived derived;
    const Base &base { derived };
    // Calls Base::GetName() instead of the virtualized Derived::GetName()
    std::cout << base.Base::getName() << '\n';

    return 0;
}
以上のようにベースBase::範囲識別子オペレータgetName()を使用してBaseのgetName()を呼び出します.

Should we make all destructors virtual?


これは新しいプログラマーがよく聞く質問です.メモリの漏洩を防ぐためには、すべてのdestructorに仮想タグを付けることが望ましい.しかし、必ずそうしなければなりませんか?
簡単に言えばそうしましょう.パフォーマンスペナルティはありますが、他のことを考えると、一番適切です
以下説明を省略する