为什么我必须申报副本&在声明析构函数时移动构造函数?

时间:2018-01-24 12:33:21

标签: c++ c++11 resharper

我有一个NonCopyable类和一个Application类派生自NonCopyable

class NonCopyable
{
public:
    NonCopyable() = default;
    virtual ~NonCopyable() = default;
    NonCopyable(NonCopyable const&) = delete;
    NonCopyable& operator =(NonCopyable const&) = delete;
    NonCopyable(NonCopyable&&) = delete;
    NonCopyable& operator=(NonCopyable&&) = delete;
};

class Application : public NonCopyable
{
public:
    ~Application() { /* ...delete stuff... */ }
};

如您所见,我不需要重新声明已删除的移动构造函数或赋值运算符,因为我已在基类中声明它们。但为什么Resharper建议我宣布其他特殊成员职能呢?它说:

  

Application定义了非默认的析构函数,但没有定义复制构造函数,复制赋值运算符,移动构造函数或移动赋值运算符[hicpp-special-member-functions]。

     

还有[cppcoreguidelines-special-member-functions]提醒,并提供相同的消息。

目前的解决方法是明确地写下所有这些内容以使警告消失:

class Application : public NonCopyable
{
public:
    ~Application() { /* ...delete stuff... */ }
    Application(Application const&) = delete;
    Application& operator =(Application const&) = delete;
    Application(Application&&) = delete;
    Application& operator=(Application&&) = delete;
};

3 个答案:

答案 0 :(得分:2)

通常,如果一个类定义了一个与编译器自动生成的析构函数不同的析构函数,那么它的目的是释放一些由该类管理的资源。

如果有一些析构函数必须释放的资源,那么通常还需要一个用户定义的构造函数来初始化该资源,这是一个用户定义的复制构造函数,用于创建由现有对象拥有的资源的副本。相同的类型,以及用户定义的移动构造函数,用于假定来自暂时存在的对象的资源。

同样,还需要一个复制赋值运算符和移动赋值运算符,这样像a = b这样的表达式可以将资源复制或移动到现有对象。

如果类没有这些构造函数或赋值运算符中的一个或多个,那么实际上,使用多个对象的代码通常不会表现一致(例如,析构函数释放资源两次,因为它由两个对象共享,导致未定义的行为等)。

当然,在某些情况下,类不需要完整的构造函数,赋值运算符和析构函数。但是,将其中一个删除通常是程序员错误(与预期效果不同),因此代码分析工具通常会抱怨此类情况。

答案 1 :(得分:1)

对于所有ReSharper都知道,你定义了一个非平凡的析构函数,并没有跟进3/5规则的其余部分。是的,默认情况下删除复制/移动构造函数,因为它们在基类中被删除,但ReSharper如何知道这是理想的行为?因此,除非您明确将它们标记为delete,否则它有必要警告您。

您可以使用pragma本地禁用警告,或者按照建议明确地delete构造函数/赋值。

作为一种解决方法,您可以引入虚拟cleanup()方法,从NonCopyable析构函数中调用它,并在派生类中覆盖它(而不是析构函数)。

答案 2 :(得分:1)

默认,删除不是继承

你错误地假设某种传递性(构造一个对象时,我总是需要使用具有类似签名的超类构造函数)。

这是一个反例:

#include <string>
#include <iostream>

class NonCopyable
{
public:
    NonCopyable() = default; // Added this little fella
    virtual ~NonCopyable() = default;
    NonCopyable(const NonCopyable &) = delete;
    NonCopyable& operator =(NonCopyable const&) = delete;
    NonCopyable(NonCopyable&&) = delete;
    NonCopyable& operator=(NonCopyable&&) = delete;
};

class Application : public NonCopyable
{
public:
    Application() = default;
    Application(const Application& a) : NonCopyable(), x(a.x){}
    ~Application() { /* ...delete stuff... */ }
    std::string x;
};

int main(){
   Application a;
   a.x = "Hello";
   Application b(a);

   std::cout << b.x << std::endl;

}

正如您所看到的,我完全能够通过使用唯一可用的超类构造函数来声明子类的复制构造函数。我还可以声明规则为5的其他构造函数。这就是为什么静态分析器会对5的规则发出警告。

结论: NonCopyable是一个无用的类。