C++赋值运算符 - 生成的编译器还是自定义?

Exp*_*ead 9 c++

我有一个中等复杂的C++类,它包含从光盘读取的一组数据.它包含浮动,整体和结构的折衷混合,现在通常使用.在主要代码审查期间,询问我们是否有自定义赋值运算符,或者我们依赖于编译器生成的版本,如果是,我们如何知道它是否正常工作?好吧,我们没有编写自定义任务,因此添加了一个单元测试来检查我们是否这样做:

CalibDataSet datasetA = getDataSet();
CalibDataSet dataSetB = datasetA;
Run Code Online (Sandbox Code Playgroud)

那么datasetB就像datasetA一样.几百行左右.现在,客户坚持认为我们不能依赖编译器(gcc)对未来的版本是正确的,我们应该编写自己的版本.他们坚持这个是正确的吗?

附加信息:

我对已经发布的答案/评论和响应时间印象深刻.另一种提出这个问题的方式可能是:POD结构/类何时成为'不'POD结构/类?

Dea*_*ing 14

众所周知,自动生成的赋值运算符将执行什么操作 - 它被定义为标准的一部分,符合标准的C++编译器将始终生成正确行为的赋值运算符(如果没有,那么它将不会符合标准的编译器).

如果您编写了自己的析构函数或复制构造函数,通常只需要编写自己的赋值运算符.如果您不需要那些,那么您也不需要赋值运算符.

  • 这被称为三法则.如果您的类管理任何资源(在析构函数中手动释放某些资源的指示符),您需要确保复制语义有效.使用[copy-and-swap idiom](http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom). (9认同)

Jer*_*fin 8

显式定义赋值运算符的最常见原因是支持"远程所有权" - 基本上是一个包含一个或多个指针的类,并拥有这些指针所引用的资源.在这种情况下,您通常需要定义赋值,复制(即复制构造函数)和销毁.这种情况有三种主要策略(按使用频率降低排序):

  1. 深拷贝
  2. 引用计数
  3. 转让所有权

深层复制意味着为分配/复制的目标分配新资源.例如,字符串类具有指向字符串内容的指针; 分配时,分配会分配一个新缓冲区来保存目标中的新内容,并将数据从源复制到目标缓冲区.这用于许多标准类的大多数当前实现中,例如std::stringstd::vector.

参考计数过去也很常见.std::string使用引用计数的许多(大多数?)旧实现.在这种情况下,不是为字符串分配和复制数据,而是简单地增加引用计数以指示引用特定数据缓冲区的字符串对象的数量.当/如果字符串的内容被修改时,您只分配了一个新缓冲区,因此它需要与其他内容不同(即,它使用写入时的副本).随着多线程,但是,你需要同步访问引用计数,这往往会对性能产生严重的影响,所以在新的代码,这是相当不寻常的(主要用于当一些商店如此多的数据,这是值得潜在浪费位CPU时间避免这样的副本).

所有权转让相对不寻常.这就是完成的事情std::auto_ptr.当您分配或复制某些内容时,分配/复制的来源基本上被销毁 - 数据从一个传输到另一个.这是(或可能)有用,但语义与正常分配完全不同,因为它通常违反直觉.同时,在适当的情况下,它可以提供高效率和简单性.的C++ 0x将拥有相当多的可管理的传输通过添加一个unique_ptr类型,使得它更加明确,并且还加入右值引用,它可以很容易地实现所有权转移的一个相当大的类的地方可以提高性能的情况下导致语义明显违反直觉.

然而,回到最初的问题,如果你没有远程所有权开始 - 也就是说,你的类不包含任何指针,你很可能不应该明确定义赋值运算符(或者dtor或者复制ctor).阻止隐式定义的赋值运算符起作用的编译器错误会阻止传递任何大量的回归测试.

即使它以某种方式确实被释放,你对它的防御也就是不使用它.没有真正的空间可以解决这样一个版本将在几个小时内被替换的问题.对于中型到大型的现有项目,您不希望切换到编译器,直到它在任何情况下广泛使用一段时间.