警告:不建议使用隐式副本构造函数的定义 [英] Warning: definition of implicit copy constructor is deprecated

查看:1352
本文介绍了警告:不建议使用隐式副本构造函数的定义的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的C ++ 11代码中有一个警告,我希望正确修复,但我真的不知道如何解决。我已经创建了自己的异常类,该异常类是从 std :: runtime_error

  class MyError:public std :: runtime_error 
{
public:
MyError(const std :: string& str,const std :: string& message)
:std :: runtime_error(消息),
str_(str)
{}

虚拟〜MyError()
{}

std: :string getStr()const
{
return str_;
}

private:
std :: string str_;
};

当我使用 / Wall 我收到以下警告:

 警告:不建议使用'MyError'的隐式副本构造函数的定义
因为它有一个用户声明的析构函数[-Wdeprecated]

所以因为我在 MyError 将不会为<$​​ c $ c> MyError 生成任何副本构造函数。我不完全了解这是否会引起任何问题...



现在我可以通过简单地删除虚拟析构函数来摆脱该警告,但是我一直认为如果基类(在本例中为 std :: runtime_error )具有虚拟析构函数,则这些类应具有虚拟析构函数。





任何想法如何最好地解决此问题?

解决方案

您不需要在派生类中显式声明析构函数:



§15.4析构函数[class.dtor] (强调我的)


可以将析构函数声明为虚拟(13.3)或纯虚拟( 13.4);如果

程序中创建了该类的任何对象或任何派生类,则应定义析构函数。如果某个类的基类
具有虚拟析构函数,则其析构函数(无论是隐式声明的用户还是
)都是虚拟的


实际上,在某些情况下,它甚至可能对性能有害,因为明确声明析构函数将防止隐式生成move构造函数和move赋值运算符。



除非您需要在析构函数中执行某些操作,否则最好的做法是忽略析构函数的显式声明。



如果您确实需要自定义析构函数,并且确定默认的复制ctor,复制赋值运算符,move ctor和move Assign运算符会为您做正确的事情,最好像这样明确地默认它们:

  MyError(const MyError&)= default; 
MyError(MyError&)=默认值;
MyError& operator =(const MyError&)=默认值;
MyError& operator =(MyError&)=默认值;

某些原因说明您为什么会看到此错误,因为这曾经是C语言中的有效代码++ 98:



从C ++ 11开始,复制构造函数的隐式生成被声明为已弃用。



§D.2复制函数的隐式声明[depr.impldec]


a的隐式定义如果该类具有用户声明的副本分配运算符
或用户声明的析构函数,则默认不推荐使用
的copy构造函数。如果该类具有
用户声明的副本构造函数或用户声明的析构函数(15.4,
15.8),则默认使用副本
赋值运算符的隐式定义(默认值)。在将来的国际标准修订版中,这些隐式定义可能会被删除(11.4)。


已知的三个规则。



以下所有报价均来自cppreference.com: https://en.cppreference.com/w/cpp/language/rule_of_three



三人制




如果类需要用户定义的析构函数,用户定义的副本
构造函数或用户定义的副本赋值运算符,则几乎
当然需要全部三个。


存在此经验法则的原因是因为默认生成的dtor,copy ctor和赋值运算符处理不同类型的资源(最值得注意的是指向内存的指针,还有其他一些指针,例如文件描述符和网络套接字,仅举几例)很少能正确地表现。如果程序员认为他需要特殊的处理才能关闭类析构函数中的文件句柄,那么他肯定会想定义应如何复制或移动此类。



为完整起见,以下是经常相关的5规则和有点争议的零规则



五规则




由于存在用户定义的析构函数,复制构造函数,
或复制分配运算符,因此无法隐式定义移动
构造函数和移动赋值运算符,任何需要使用
语义的类,都必须声明所有五个特殊成员
函数:




Rule的零




具有自定义析构函数,复制/移动构造函数或
复制/移动赋值运算符的类应专门处理所有权
(遵循单一责任原则)。其他
类不应具有自定义析构函数,复制/移动构造函数或
复制/移动赋值运算符。



I have a warning in my C++11 code that I would like to fix correctly but I don't really know how. I have created my own exception class that is derived from std::runtime_error:

class MyError : public std::runtime_error
{
public:
    MyError(const std::string& str, const std::string& message)
      : std::runtime_error(message),
        str_(str)
    { }

    virtual ~MyError()
    { }

    std::string getStr() const
    {
        return str_;
    }

  private:
      std::string str_;
};

When I compile that code with clang-cl using /Wall I get the following warning:

warning: definition of implicit copy constructor for 'MyError' is deprecated 
         because it has a user-declared destructor [-Wdeprecated]

So because I have defined a destructor in MyError no copy constructor will be generated for MyError. I don't fully understand if this will cause any issues...

Now I could get rid of that warning by simply removing the virtual destructor but I always thought that derived classes should have virtual destructors if the base class (in this case std::runtime_error) has a virtual destructor.

Hence I guess it is better not to remove the virtual destructor but to define the copy constructor. But if I need to define the copy constructor maybe I should also define the copy assignment operator and the move constructor and the move assignment operator. But this seems overkill for my simple exception class!?

Any ideas how to best fix this issue?

解决方案

You do not need to explicitly declare the destructor in a derived class:

§ 15.4 Destructors [class.dtor] (emphasis mine)

A destructor can be declared virtual (13.3) or pure virtual (13.4); if any objects of that class or any derived class are created in the program, the destructor shall be defined. If a class has a base class with a virtual destructor, its destructor (whether user- or implicitly-declared) is virtual.

In fact, it might even be detrimental to performance in some cases, as explicitly declaring a destructor will prevent the implicit generation of a move constructor and move assignment operator.

Unless you need to do something in your destructor, the best course of action would be to just omit an explicit declaration of a destructor.

If you do need a custom destructor, and are certain that the default copy ctor, copy assignment operator, move ctor and move assignment operator would do the correct thing for you, it is best to explicitly default them like so:

MyError(const MyError&) = default;
MyError(MyError&&) = default;
MyError& operator=(const MyError&) = default;
MyError& operator=(MyError&&) = default;

Some reasoning on why you're seeing the error, because this used to be perfeclty valid code in C++98:

As of C++11, implicit generation of the copy constructor is declared as deprecated.

§ D.2 Implicit declaration of copy functions [depr.impldec]

The implicit definition of a copy constructor as defaulted is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor. The implicit definition of a copy assignment operator as defaulted is deprecated if the class has a user-declared copy constructor or a user-declared destructor (15.4, 15.8). In a future revision of this International Standard, these implicit definitions could become deleted (11.4).

The rationale behind this text is the well-known Rule of three.

All quotes below are sourced from cppreference.com: https://en.cppreference.com/w/cpp/language/rule_of_three

Rule of Three

If a class requires a user-defined destructor, a user-defined copy constructor, or a user-defined copy assignment operator, it almost certainly requires all three.

The reason why this rule of thumb exists is because the default generated dtor, copy ctor and assignment operator for handling different types of resources (most notably pointers to memory, but also others, like file descriptors and network sockets to name just a couple) rarely do the correct behaviour. If the programmer thought that he needed special handling for the closing of a file handle in the class destructor, he most surely wants to define how this class should be copied or moved.

For completeness, below are the often related Rule of 5, and the somewhat disputed Rule of Zero

Rule of Five

Because the presence of a user-defined destructor, copy-constructor, or copy-assignment operator prevents implicit definition of the move constructor and the move assignment operator, any class for which move semantics are desirable, has to declare all five special member functions:

Rule of Zero

Classes that have custom destructors, copy/move constructors or copy/move assignment operators should deal exclusively with ownership (which follows from the Single Responsibility Principle). Other classes should not have custom destructors, copy/move constructors or copy/move assignment operators.

这篇关于警告:不建议使用隐式副本构造函数的定义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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