为什么我必须在声明析构函数时声明复制和移动构造函数?

问题描述 投票:6回答:3

我有一个来自NonCopyableApplication类和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;
};
c++ c++11 resharper
3个回答
2
投票

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

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

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

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

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


1
投票

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

您可以使用pragma或delete本地禁用警告,如您所建议的那样明确构造函数/赋值。

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


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是一个无用的类。

© www.soinside.com 2019 - 2024. All rights reserved.