C ++接口必须遵守五个规则吗? [英] Must a c++ interface obey the rule of five?

查看:300
本文介绍了C ++接口必须遵守五个规则吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

定义接口类时声明实例化方法的正确方法是什么?



出于明显的原因,抽象基类必须具有虚拟析构函数。但是,将给出以下编译警告:'InterfaceClass'定义了非默认析构函数,但未定义副本构造函数,副本赋值运算符,move构造函数或move
赋值运算符,即五规则。



我理解为什么通常应遵循五规则,但是它仍然适用于抽象基类或接口吗? / p>

我的意思是:

  class InterfaceClass 
{
// ==实例==
受保护:
//-构造函数-
InterfaceClass()=默认值;
InterfaceClass(const InterfaceClass&)=默认值;
InterfaceClass(InterfaceClass&)=默认值;

public:
//-析构函数-
virtual〜InterfaceClass()= 0;


// ==运算符==
受保护:
//-分配-
InterfaceClass& operator =(const InterfaceClass&)=默认值;
InterfaceClass& operator =(InterfaceClass&)=默认值;


// ==方法==
public:
//这里有一些纯接口方法...
};



// ==实例化==
//-析构函数-
InterfaceClass ::〜InterfaceClass()
{
}

这是正确的吗?这些方法应该改为 = delete 吗?是否有某种方法可以将析构函数声明为虚拟纯函数,同时还能以某种方式保持默认值?



即使我将析构函数声明为: virtual〜InterfaceClass ()= default; ,如果我没有明确默认其他四个,则将得到相同的编译器警告。



Tl; dr:满足接口类五个规则的正确方法是什么,因为用户必须定义​​一个虚拟析构函数。



感谢您的时间和帮助!

解决方案


这是正确的吗?这些方法应该改为= delete吗?


您的代码似乎正确。当您尝试多态复制派生类时,很明显需要将特殊的复制/移动成员函数定义为默认值和受保护。考虑以下附加代码:

  #include< iostream> 

class ImplementationClass:公共InterfaceClass
{
private:
int数据;
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;
//多态副本
* B_p = * A_p;
B.print();
//常规副本
B = A;
B.print();
返回0;
}

并考虑4个用于在InterfaceClass中定义特殊复制/移动成员函数的选项。


  1. 复制/移动成员函数= 删除

在InterfaceClass中删除特殊的复制/移动成员函数后,可以防止多态复制:

  * B_p = * A_p; //无法编译,在InterfaceClass 

中删除副本这很好,因为多态副本也不会



另一方面,您也可以防止普通复制,因为编译器将无法隐式生成一个没有基类的拷贝赋值运算符拷贝赋值运算符:

  B = A; //也不会编译,副本分配将在ImplementationClass 



    $ b $中删除b
  1. 复制/移动特殊成员函数public

默认情况下复制/移动特殊成员函数对于公共(或未定义复制/移动成员函数)的普通复制将起作用:

  B = A; //将复制并正常工作

,但多态副本将被启用并导致切片:

  * B_p = * A_p; //将编译但不复制派生类中的额外数据成员。 




  1. 复制/移动特殊成员函数未定义

如果未定义move& copy特殊成员函数,则复制行为类似于2:编译器将隐式生成不赞成使用的副本特殊成员(导致多态切片)。但是,在这种情况下,编译器不会隐式生成移动特殊成员,因此将在可能进行移动的地方使用复制。


  1. 受保护的复制/移动成员功能(您的建议)

使用特殊的复制/移动成员功能作为默认值并受保护,如您的示例所示,您将防止多态复制,这可能会导致切片:

  * B_p = * A_p; //将不会编译,副本将在InterfaceClass 

中受到保护,但是编译将显式生成默认副本赋值运算符InterfaceClass和ImplementationClass将能够隐式生成其副本赋值运算符:

  B = A; //将正确编译并正常工作

因此,您的方法似乎是最好,最安全的选择


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!

解决方案

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 nor be able co 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 copile and work correctly

but polymorphic copy would be enabled and lead to slicing:

*B_p = *A_p; // will compile but 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 may lead to slicing:

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

However, the compile will explicitly generate a default copy assignment operator 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

这篇关于C ++接口必须遵守五个规则吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆