Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Must a c++ interface obey the rule of five?

What is the correct way to declare instantiation methods when defining an interface class?

Abstract base classes are required to have a virtual destructor for obvious reasons. However, the following compilation warning is then given: "'InterfaceClass' defines a non-default destructor but does not define a copy constructor, a copy assignment operator, a move constructor or a move assignment operator", which is the 'rule of five'.

I understand why the 'rule of five' should be obeyed in general, but is it still applicable for an abstract base class or interface?

My implimentation is then:

class InterfaceClass
{
    //  == INSTANTIATION ==
  protected:
    //  -- Constructors --
    InterfaceClass()                      = default;
    InterfaceClass(const InterfaceClass&) = default;
    InterfaceClass(InterfaceClass&&)      = default;

  public:
    //  -- Destructors --
    virtual ~InterfaceClass() = 0;


    //  == OPERATORS ==
  protected:
    //  -- Assignment --
    InterfaceClass& operator=(const InterfaceClass&) = default;
    InterfaceClass& operator=(InterfaceClass&&)      = default;


    //  == METHODS ==
  public:
    // Some pure interface methods here...
};



//  == INSTANTIATION ==
//  -- Destructors --
InterfaceClass::~InterfaceClass()
{
}

Is this correct? Should these methods be = delete instead? Is there some way of declaring the destructor to be virtual pure whilst also somehow remaining default?

Even if I declare the destructor as: virtual ~InterfaceClass() = default;, if I do not explicitly default the other four then I will get the same compiler warning.

Tl;dr: What is the correct way to satisfy the 'rule of five' for an interface class as the user must define a virtual destructor.

Thanks for your time and help!

like image 473
user7119460 Avatar asked Apr 22 '18 01:04

user7119460


People also ask

What is the rule of five in C++?

The Rule of Five is a programming concept brought about in C++11. It originates from the Rule of Three, where the introduction of Move Semantics in C++11, caused the Rule Of Three to expand and become the Rule Of Five. Before we talk about the Rule of Five however, we will briefly talk about the Rule Of Three to understand it’s base.

What is the rule of three in C++?

Firstly, the rule of three specifies that if a class implements any of the following functions, it should implement all of them: 1 copy constructor 2 copy assignment operator 3 destructor More ...

How many default operation rules does C++ have?

This post is about the rule of zero, five, or maybe six. I will also show the difference between copy and reference semantic and a quite similar topic: deep versus shallow copy. To be precise, C++ has about 50 rules for managing the lifecycle of an object. This time I will write about the three very important default operation rules.

What is the rule of five in Java?

The Rule of Five is a modern extension to the Rule of Three. The Rule of Five states that if a type ever needs one of the following, then it must have all five. In addition to copy semantics (Rule of Three), we also have to implement move semantics.


1 Answers

Is this correct? Should these methods be = delete instead?

Your code seems correct. The need of defining special copy/move member functions as default and protected comes clear when you try to copy a derived class polymorphycally. Consider this additional code:

#include <iostream>

class ImplementationClass : public InterfaceClass
{
  private:
    int data;
  public:
    ImplementationClass()
    {
        data=0;    
    };
    ImplementationClass(int p_data)
    {
        data=p_data;
    };
    void print()
    {
        std::cout<<data<<std::endl;
    };
};


int main()
{
    ImplementationClass A{1};
    ImplementationClass B{2};
    InterfaceClass *A_p = &A;
    InterfaceClass *B_p = &B;
    // polymorphic copy
    *B_p=*A_p;
    B.print();
    // regular copy
    B=A;
    B.print();
    return 0;
}
   

And consider 4 options for defining special copy/move member functions in your InterfaceClass.

  1. copy/move member functions = delete

With special copy/move member functions deleted in your InterfaceClass, you would prevent polymorphic copy:

*B_p = *A_p; // would not compile, copy is deleted in InterfaceClass

This is good, because polymorphic copy would not be able to copy the data member in the derived class.

On the other hand, you would also prevent normal copy, as the compiler won't be able to implicitly generate a copy assignment operator without the base class copy assignment operator:

B = A; //  would not compile either, copy assignment is deleted in ImplementationClass 
  1. copy/move special member functions public

With copy/move special member functions as default and public, (or without defining copy/move member functions), normal copy would work:

B = A; //will compile and work correctly

but polymorphic copy would be enabled and lead to slicing:

*B_p = *A_p; // will compile but will not copy the extra data members in the derived class. 
  1. copy/move special member functions not defined

If move&copy special member functions are not defined, behavior with respect to copy is similar to 2: the compiler will implicitly generate deprecated copy special members (leading to polymorphic slicing). However in this case the compiler will not implicitly generate move special members, so copy will be used where a move would be possible.

  1. protected copy/move member functions (your proposal)

With special copy/move member functions as default and protected, as in your example, you will prevent polymorphic copy which would otherwise had lead to slicing:

*B_p = *A_p; // will not compile, copy is protected in InterfaceClass

However, the compiler will explicitly generate a default copy assignment operator for InterfaceClass, and ImplementationClass will be able to implicitly generate its copy assignment operator:

B = A; //will compile and work correctly

So your approach seems the best and safest alternative

like image 79
Gianni Avatar answered Sep 23 '22 12:09

Gianni