c++rule-of-five

How to handle conflict between virtual destructor and "Rule of 0"?


"Rule of 0" says that we should not declare destructors, copy/move-constructors, and copy/move-assignments for classes that do not manage resources ownership.

But, when we create a base class, we need to declare a virtual destructor for the sake of proper destruction behavior of descendants.

Then, the question is: When a base class is not devised to manage resources, what should we do?


Solution

  • That stating of the rule of zero lacks nuance1.

    You should prefer to define none of the five, but if you do define any of them, you should define all of them.

    For a polymorphic base class Base, it's a fairly easy. ~Base has to be defined, because you need it to be virtual. As it turns out, you want to think about copying and moving, because they generally aren't appropriate. What should a Derived1 instance do to copy (or move from) a Derived2 instance?

    So you end up with:

    // No copy or moves allowed
    class Base {
    public:
        virtual ~Base() = default;
        Base(const Base&) = delete;
        Base(Base&&) = delete;
        Base& operator=(const Base&) = delete;
        Base& operator=(Base&&) = delete;
    };
    
    // Subclasses can allow copy or move
    class Base {
    public:
        virtual ~Base() = default;
    protected:
        Base(const Base&) = default;
        Base(Base&&) = default;
        Base& operator=(const Base&) = default;
        Base& operator=(Base&&) = default;
    };
    
    1. You could, at a stretch, express that "the most derived type of the object" is a resource that Base is managing, and then you do fit inside your stating of the rule of zero.