宏禁止类复制和分配。谷歌-vs- Qt

时间:2009-09-21 13:02:53

标签: c++

要禁止复制或分配类,通常的做法是制作复制构造函数 和赋值运算符私有。谷歌和Qt都有宏,使这个变得简单明了。 这些宏是:

谷歌:

#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
  TypeName(const TypeName&);   \
  void operator=(const TypeName&) 

Qt:

#define Q_DISABLE_COPY(Class) \
  Class(const Class &); \     
  Class &operator=(const Class &);

问题: 为什么两个赋值运算符的签名不同?好像Qt版本是正确的。 这两者之间的实际区别是什么?

11 个答案:

答案 0 :(得分:43)

没关系。返回类型不是函数签名的一部分,因为它不参与重载解析。因此,当您尝试执行赋值时,无论您是否使用返回类型,两个声明都将匹配。

由于这些宏中的所有内容都是函数永远不会被调用,因此返回void并不重要。

答案 1 :(得分:15)

我只想提一下,有一种替代策略可以实现抽象,以禁止复制和分配类。我们的想法是使用继承而不是预处理器。我个人更喜欢这种方法,因为我遵循经验法则,尽可能避免使用预处理器。

boost::noncopyable是一个示例实现。它使用如下:

class A : noncopyable
{
    ...
};

答案 2 :(得分:9)

参见Boost.Utility,特别是boost::noncopyable。它不是宏,而是具有私有副本和赋值的基类。它可以防止编译器在派生类中生成隐式副本和赋值。

编辑:对不起,这不是原始问题的答案。顺便说一句,boost :: noncopyable使用const引用作为赋值运算符的返回类型。我的印象是返回值的类型无关紧要,因为它不应该被使用。尽管如此,将运算符设为私有并不会阻止在类或朋友中使用,在这种情况下,非常规的返回类型(如void,const引用等)可能会导致编译错误并捕获其他错误。

答案 3 :(得分:5)

没有实际的区别。赋值运算符签名在风格上有所不同。通常让赋值运算符返回允许链接的引用:

a = b = c;

但是返回void的版本也是合法的,对于只是声明操作符private并因此禁止使用的唯一目的的情况,它将正常工作。

答案 4 :(得分:4)

从标准,12.8,第9节:“用户声明的复制赋值运算符X::operator=是类X的非静态非模板成员函数,其中只有一个X类型的参数1}},X&,const X&,volatile X&或const volatile X&。“它没有说明返回类型,因此任何返回类型都是允许的。

第10条说“如果类定义没有显式声明一个复制赋值运算符,则会隐式声明一个。”

因此,声明任何X::operator=(const X&)(或任何其他指定的赋值类型)就足够了。如果永远不会使用操作员,则身体和返回类型都不重要。

因此,这是一种风格上的差异,一个宏观做了我们可能期望的事情,一个节省了一些角色,并以一种可能让某些人感到惊讶的方式完成工作。我认为Qt宏在风格上更好。既然我们正在谈论宏,我们不是在谈论程序员必须输入任何额外的东西,而且不会让人惊讶在语言结构中是一件好事。

答案 5 :(得分:2)

其他人已经回答了为什么为operator =设置不同的返回值是合法的;恕我直言jalf said it best

但是,您可能想知道为什么 Google使用不同的返回类型,我怀疑是这样的:

在禁用此类赋值运算符时,您不必重复类型名称。通常,类型名称是声明中最长的部分。

当然,这个原因是无效的,因为使用了一个宏但仍旧 - 老习惯很难。 : - )

答案 6 :(得分:1)

两者都有相同的用途

写完这篇文章之后:

Class &operator=(const Class &);

您将获得连锁分配的好处。但在这种情况下,您希望赋值运算符是私有的。所以没关系。

答案 7 :(得分:1)

Qt版本向后兼容,而谷歌则不是。

如果您在完全删除它之前开发了库并弃用了赋值,那么在Qt中它很可能会保留它原来拥有的签名。在这种情况下,较旧的应用程序将继续使用新版本的库运行(但是,它们不会使用较新版本编译)。

Google的宏没有这样的属性。

答案 8 :(得分:1)

正如其他几个答案所提到的,函数的返回类型不参与函数签名,因此两个声明都是等效的,只要使赋值运算符不被类的客户端使用。

我个人更喜欢让一个类私有地从一个空的非可复制基类继承的习惯用法(比如boost :: noncopyable,但我有自己的,所以我可以在没有提升的项目中使用它)。空基类优化负责确保零开销,并且它简单易读,并且不依赖于可怕的预处理器宏功能。

它还有一个优点,即复制和赋值甚至不能在类实现代码中使用 - 它会在编译时失败,而这些宏在链接时会失败(可能是信息量较少的错误消息)。 p>

答案 9 :(得分:0)

顺便说一句,如果你有权访问Boost库(你没有?为什么不呢?),实用程序库已经有noncopyable class很长一段时间了:

class YourNonCopyableClass : boost::noncopyable {

更清晰的恕我直言。

答案 10 :(得分:-1)

实际上,我要说的是,如果您有C ++ 11编译器,则不应再使用两者。

您应该改为使用删除功能,请参见此处

Meaning of = delete after function declaration

在这里

http://www.stroustrup.com/C++11FAQ.html#default

为什么:基本上是因为编译器消息更加清晰。当编译器需要复制或复制分配运算符之一时,它会立即指出=delete所在的行。

更好和更完整的解释也可以在 Scott Meyers Effective Modern C ++ 书中的第11项中:将已删除的函数优先于私有的未定义函数。 >