59

在 C++11 中显式删除了成员函数,从不可复制的基类继承是否仍然值得?

我说的是你私下继承基类的技巧,该基类具有私有或已删除的复制构造函数和复制赋值(例如boost::noncopyable)。

这个问题中提出的优点是否仍然适用于 C++11?


我不明白为什么有些人声称在 C++11 中使类不可复制更容易。

在 C++03 中:

private:
    MyClass(const MyClass&) {}
    MyClass& operator=(const MyClass&) {}

在 C++11 中:

MyClass(const MyClass&) = delete;
MyClass& operator=(const MyClass&) = delete;

编辑:

正如许多人所指出的,为私有复制构造函数和复制赋值运算符提供空主体(即 {})是错误的,因为这将允许类本身调用这些运算符而不会出现任何错误。我一开始没有添加 {},但遇到了一些链接器问题,导致我出于某种愚蠢的原因添加了 {}(我不记得当时的情况)。我知道更好知道。:-)

4

5 回答 5

76

嗯,这个:

private:
    MyClass(const MyClass&) {}
    MyClass& operator=(const MyClass&) {}

在技​​术上仍然允许MyClass会员和朋友复制。当然,理论上这些类型和功能都在您的控制之下,但该类仍然是可复制的。至少使用boost::noncopyableand = delete没有人可以复制课程。


我不明白为什么有些人声称在 C++11 中使类不可复制更容易。

它不是“更容易”而是“更容易消化”。

考虑一下:

class MyClass
{
private:
    MyClass(const MyClass&) {}
    MyClass& operator=(const MyClass&) {}
};

如果您是一名 C++ 程序员,阅读过 C++ 的介绍性文本,但很少接触惯用的 C++(即:很多C++ 程序员),那么这...令人困惑。它声明了复制构造函数和复制赋值运算符,但它们是空的。那么为什么要声明它们呢?是的,它们是private,但这只会引发更多问题:为什么要将它们设为私有?

要理解为什么这会阻止复制,您必须意识到通过将它们声明为私有,您可以使非会员/朋友无法复制它。这对新手来说不是很明显。当他们尝试复制它时也不会收到错误消息。

现在,将其与 C++11 版本进行比较:

class MyClass
{
public:
    MyClass(const MyClass&) = delete;
    MyClass& operator=(const MyClass&) = delete;
};

如何理解这个类不能被复制?无非就是理解= delete语法的含义。任何解释 C++11 语法规则的书都会准确地告诉你它的作用。这段代码的效果对于没有经验的 C++ 用户来说是显而易见的。

这个成语的伟大之处在于它变成了一个成语,因为它是表达你的意思的最清晰、最明显的方式。

甚至boost::noncopyable需要更多的思考。是的,它被称为“不可复制”,所以它是自我记录的。但如果你以前从未见过它,它就会提出问题。你为什么要从无法复制的东西中派生出来?为什么我的错误信息会提到boost::noncopyable的复制构造函数?等等。同样,理解成语需要更多的脑力劳动。

于 2012-02-27T01:05:00.490 回答
24

第一件事是,正如我之前的其他人指出的那样,您的习语错误,您声明为私有而不定义

class noncopyable {
   noncopyable( noncopyable const & );
   noncopyable& operator=( noncopyable const & );
};

的返回类型operator=基本上可以是任何东西。此时,如果您在标题中阅读该代码,它实际上是什么意思?只能被朋友复制还是永远不能复制?请注意,如果您在示例中提供定义,则说明I 只能在课堂内和由朋友复制,缺少定义将其翻译为I cannot be mapped。但是头文件中缺少定义并不是到处都缺少定义的同义词,因为它可以在 cpp 文件中定义。

这就是从名为noncopyable的类型继承的地方,它明确表明意图是避免复制,就像如果您手动编写上面的代码,您应该在意图禁用复制的行添加注释。

C++11 并没有改变这一切,它只是在代码中明确说明了文档。在您将复制构造函数声明为已删除的同一行中,您记录了您希望它完全禁用

作为最后的评论,C++11 中的特性不仅仅是能够用更少的代码或更好的代码编写不可复制的代码,而是禁止编译器生成您不希望生成的代码。这只是该功能的一种用途。

于 2012-02-27T01:26:19.027 回答
10

除了其他人提出的要点...

拥有未定义的私有复制构造函数和复制赋值运算符可防止任何人进行复制。但是,如果成员函数或友元函数尝试进行复制,它们将收到链接时错误。如果他们试图在您明确删除这些函数的地方这样做,他们将收到编译时错误。

我总是让我的错误尽快发生。使运行时错误发生在错误点而不是稍后发生(因此当您更改变量而不是读取它时使错误发生)。将所有运行时错误变成链接时错误,这样代码就永远不会出错。将所有链接时错误变成编译时错误,以加快开发速度并提供更有用的错误消息。

于 2012-08-13T01:00:39.200 回答
5

它更具可读性,并允许编译器给出更好的错误。

“删除”对读者来说更清楚,特别是如果他们正在浏览课程。同样,编译器可以告诉您您正在尝试复制不可复制的类型,而不是给您一个通用的“尝试访问私有成员”错误。

但实际上,这只是一个便利功能。

于 2012-02-27T00:53:18.480 回答
2

这里的人建议您声明成员函数而不定义它们。我想指出,这种方法是不可移植的。一些编译器/链接器要求如果你声明一个成员函数,那么你也必须定义它,即使它没有被使用。如果您只使用 VC++、GCC、clang,那么您可以摆脱这种情况,但如果您尝试编写真正可移植的代码,那么其他一些编译器(例如 Green Hills)将会失败。

于 2014-01-07T23:04:10.233 回答