weno
weno

Reputation: 856

Base class virtual destructor - rule of five?

I have a base State interface class with virtual default destructor.

class State {
public:
    virtual void event() = 0;

    virtual ~State() = default; // relevant part

    virtual void onCreate() {}
    virtual void onDestroy() {}
    virtual void onActivate() {}
    virtual void onDeactivate() {}
};

And then some classes inheriting from it:

class GameState : public State {
public:
    void event() override;
    // ...
};

class MenuState : public State {
public:
    void event() override;
    // ...
};

Compiler generates default move operations if no copy operation or destructor is user-defined. Compiler generates default copy operations if no move operation is user-defined.

  1. Am I correct that by declaring virtual default destructor I have effectively deleted the default move operations?

  2. Will the move operations work for the deriving classes if the base class had implicitly deleted its move operations, and the base class is just an interface without data members?

  3. Is it really sensible to follow the Rule of 5 here? It seems quite a bloat to explicitly delete or default all 5 special member functions.

Upvotes: 3

Views: 638

Answers (1)

Jens
Jens

Reputation: 375

Let's assume your base class has copy (and possibly move) constructors and assignment operators. What would you expect in this case?

MenuState menuState;
State state = menuState; // be aware that this State object is not a MenuState any more

In case your State class is abstract (if for example onActivate() was pure virtual) above wouldn't make any sense because a State instance would not be allowed.

But even if not, any MenuState members would be sliced, when you put them into a State object. If instances of your base class don't make much sense on their own, copy and move constructors and assignment operators should rather be deleted.

For derived (concrete) classes copying or moving might make more sense. In this case it can be useful to have protected copy constructors and assignment operators in the base class. So you cannot instantiate your base class itself, but your derived classes can make use of it.

MenuState menuState;
MenuState copy = menuState; // might be useful

If on the other hand the base class makes sense on its own, public (and possibly default) copy and move constructors and assignment operators can be reasonable. This can be the case if derived classes just add some additional information, which is not required whenever you convert your derived objects to base objects.

So it really depends, and that's why it's better to explicitly point out what is intended, public copy and move, protected copy and move, or non at all.

Such class hierarchies, however, are often used with pointers (like std::unique_ptr or std::shared_ptr). Then a virtual clone() method sometimes is more useful.

auto state = std::make_unique<MenuState>();
std::unique_ptr<State> copy = state->clone();

Internally this clone() method might use the copy constructor, though, which can be public (be careful of slicing, if your derived classes are not final) or protected (if you want to make sure only the clone() method is used).

Upvotes: 3

Related Questions