防止 C++ 中的意外对象复制
Posted
技术标签:
【中文标题】防止 C++ 中的意外对象复制【英文标题】:prevent accidental object copying in C++ 【发布时间】:2010-11-23 07:40:10 【问题描述】:在我们公司的编码标准中,我们被告知要“注意可以防止(意外)复制的方法”。
我不太确定这意味着什么,但假设它们的意思是如果不需要,我们应该停止复制类。
我能想到的如下:
-
将类的复制构造函数设为私有。
将类的赋值运算符 (operator=) 设为私有。
明确类的构造函数(以防止使用不正确的变量创建类)。
对于所有执行内存分配和需要复制的类,请确保复制构造函数和赋值运算符执行深复制而不是浅复制。
我在正确的轨道上吗?有什么我可能错过的吗?
【问题讨论】:
实际上,我会理解这句话的意思是您应该注意这些事情并注意不要意外复制不应该复制的对象。但我知道什么?无论如何,这样的问题更有趣。 【参考方案1】:是的,将赋值运算符和复制构造函数设为私有将阻止您使用标准方法创建对象的任何副本(但如果您确实需要一个对象的副本,您可以实现,例如 Copy() 方法,该方法将执行深拷贝)。
看看boost::noncopyable。
更新(回复 Tal Pressman):
...您应该注意这些事情并注意不要意外复制不应该复制的对象。
好吧,我想,任何意外复制都将使用赋值运算符或复制构造函数来执行。所以将它们设为私有实际上是有道理的:如果对象复制是昂贵的操作,那么复制必须是显式的:其他开发人员可以无意间间接调用复制操作,编译器会通知他,这是禁止的。
【讨论】:
【参考方案2】:如果您的编码标准规定“注意可以防止(意外)复制的方式”,我猜他们不仅仅是在谈论防止类本身的复制,而是关于不必要的性能影响 / 使用类时意外复制。 C++ 新手在代码中不必要地浪费性能的主要原因之一是不必要的复制,通常是通过临时复制。编译器在决定何时不需要临时文件方面越来越好(请参阅"Want speed? Pass by Value",感谢 Konrad 的评论),但最好的办法是学习了解 C++ 中复制和临时文件的内部工作原理(以及其他)。对我来说,阅读 Efficient C++ 真的让我开始了。
【讨论】:
在考虑复制省略和 NRVO 时,您的第二点在很大程度上是错误的/误导性的。让专业人士说话:cpp-next.com/archive/2009/08/want-speed-pass-by-value 这实际上是一篇很棒的文章 - 我知道 RVO,但我上次深入研究它时仍在使用 VS2003,依赖编译器来让它正确处理更复杂的问题是有风险的类型。很高兴听到 C++0X 的问题越来越少 @MadKeithV:不要被提到的 C++0x 误导:那篇文章中的一切适用于当前的编译器和当前版本的 C++,没有什么是新的到 C++0x。 规范规则应该是“如有疑问,请检查汇编语言输出”:)。【参考方案3】:如果您使用的是 boost,那么防止类被复制的最简单方法是从 noncopyable 派生您的类:
#include <boost/noncopyable.hpp>
class Foo : private boost::noncopyable
它比手动将复制构造函数和赋值运算符设为私有更清晰,并且具有相同的结果。
【讨论】:
我不认为他想阻止类被复制,而是他想防止它被意外地复制。 我相信他会这样做,因为他将复制构造函数列为私有的选项之一。 尽管我很喜欢 boost,但我遇到过一些根本不允许使用 boost 的工作情况。【参考方案4】:你在正确的轨道上。如果您不想使用 boost,您可以执行以下操作:将复制构造函数和复制赋值运算符设为私有并且不要实现它们。因此,如果您尝试复制实例,则会出现编译器错误。
【讨论】:
【参考方案5】:从避免错误的角度来看,您的列表看起来很棒,例如由于对象隐式复制的共享指针,多次删除同一内存区域。
我希望这也是相关的;从“注意可以防止(意外)复制的方式”的声明中,您可能会认为这意味着“注意无意的不必要复制”。这将意味着可能影响性能的情况。在一个非常简单的示例中,您的编码约定可能意味着您应该更喜欢:
std::string text1( "some text" );
结束:
std::string text1 = "some text";
第二种情况会导致在调用赋值运算符(一种复制形式)之前创建一个临时字符串来保存“一些文本”,以用“一些文本”填充 text1。显然这是一个微不足道的例子,良好的实践表明你应该尽可能使用构造函数初始化(第一个例子)。
【讨论】:
感谢您的评论。这是我不知道的事情。因此,如果需要类型转换,请始终调用构造函数而不是使用赋值。 我不确定你对这个是否正确。两者都创建一个临时 char[]. 不,这完全是错误的。在这种情况下,所有现代编译器都会避免不必要的std::string
临时实例。
@the_drow - 两者都创建了一个临时的 const char[] 但只有第二个示例创建了一个不必要的临时 std::string。 @Konrad - 所有现代编译器应该避免它,但并非所有编译器都是现代的,也不需要优化它。据我所知,它不在标准范围内,但我可能是错的。
@J.Churchill:标准的 §12.2.2 明确提到实现可能会省略不必要的副本。所有中途现代编译器做优化它。这可能意味着自 VC6 以来的所有编译器。所以从纯理论的角度来看,你是对的。但实际上并非如此。以上是关于防止 C++ 中的意外对象复制的主要内容,如果未能解决你的问题,请参考以下文章