我想知道为什么编译器提供默认的复制构造函数。这个想法背后的策略是什么。
先谢谢。
答案 0 :(得分:27)
来自相关(但不完全相同)的问题 - Why don't C++ compilers define operator== and operator!=?:
Stroustrup对“C ++的设计和演变”(第11.4.1节 - 复制控制)中的默认复制构造函数说:
我个人认为很遗憾,默认情况下定义了复制操作,我禁止复制许多类的对象。但是,C ++从C继承了它的默认赋值和复制构造函数,并且经常使用它们。
所以答案是Stroustrup不情愿地将其与C语言向后兼容(可能是大多数C ++瑕疵的原因,但也可能是C ++流行的主要原因)。
出于我自己的目的,在我的IDE中,我用于新类的片段包含私有赋值运算符和复制构造函数的声明,这样当我生成一个新类时,我没有默认赋值和复制操作 - 我必须明确如果我希望编译器能够为我生成它们,请从private:section中删除这些操作的声明。
答案 1 :(得分:8)
从The C++ Programming Language开始,第11.3.4节“复制”
...对于默认复制构造函数具有正确语义的类型,我更喜欢依赖于该默认值。它比我能写的任何东西都要简洁,人们应该理解默认值。此外,编译器了解默认值及其可能的优化机会。此外,手动写出成员副本对于具有许多数据成员的类来说是繁琐且容易出错的。
基本上,我读到这一点,因为默认的复制构造函数可以节省您的工作量,使您免于因繁琐而导致的错误,并通过消除手动优化它的诱惑(通过让编译器这样做)来帮助优化代码
答案 2 :(得分:6)
因为否则,当您按值传递实例时,编译器如何生成一个?
答案 3 :(得分:3)
我不知道为什么它最初设计的方式。但作为一个用户,我可以说为什么我很高兴他们做到了。
我对默认赋值运算符的看法相同。大多数人要么a)错误地定义他们的拷贝构造函数/ equals运算符,要么无法维护它。我通过切换到RAII类型并删除手动编码的运算符/复制构造函数,删除了C ++代码中的大量错误。
答案 4 :(得分:3)
几点:
能够控制是否可以复制对象非常重要。
如果您不希望复制对象,则可以将复制构造函数声明为私有(并且在C ++ 0x中您可以说=delete
)。这与你的提议没有变化,但问题只是逆转,即。你可以预见一个类似的问题:“为什么编译器在知道该怎么做时不会生成默认的复制构造函数?”
这也将在C ++ 0x中解决,因为我相信有一个=default
标志,允许您指定它:
class C {
public:
C (C const &) = default;
};
允许编译器实现最佳版本的复制构造函数是有益的。
除了易用性之外,今天编译器能够选择最有效的方法来复制对象。例如,如果它知道这样做是安全的,那么它可能只对对象使用memcpy。
根据您的建议,为了实现今天类似的优化,编译器需要分析构造函数体,以验证除了浅层复制所有成员之外什么都不做。这不仅非常重要,通常只有在构造函数体对所有翻译单元可见时才会发生。
在C ++中,0x =default
绕过这个。
如果对于C ++ 0x,编译器和静态分析工具开始生成有关“旧式隐式默认成员”的警告,我不会感到惊讶。
答案 5 :(得分:1)
节省您的时间?如果你有一个简单的类(即如果你可以轻松地复制构造它的所有元素),那么你就不需要写一个。
答案 6 :(得分:1)
如果C代码使用struct
,则需要使用默认的复制构造函数来保留C struct复制语义。
答案 7 :(得分:0)
我不确定“官方线”是什么(我附近没有Strastroup的书),我相信有人会把它挖出来。
但是,在大多数情况下,浅拷贝和默认初始化是“足够好”,所以它是 编译器提供它们比让开发者明确地编写它们更好。
如果开发人员编写了这个简单的复制构造函数并且稍后字段发生了变化,则由该用户进行更改并且如果他忘记了可能存在严重错误(例如,这些字段构造为什么?)。
只有让用户在真正需要做一些奇特的事情(如深度复制)时编写复制构造函数,才能减少这些错误的发生频率。
答案 8 :(得分:0)
我认为你是从错误的方向前进的 - 如果编译器可以为每个类或结构编写一个完整且正确的'默认'复制构造函数,那么没有人会抱怨,是吗?问题是,编译器无法可靠地执行此操作,因此对于这些情况,您需要编写自己的编译器,覆盖默认值。
答案 9 :(得分:0)
因为C ++不是垃圾收集,它会强制你跟踪所有指针所有者,这实际上是不可能做到的,除非你通过在整个地方使用大量复制来简化问题,所以至少你知道谁拥有副本,顺便使你的程序比垃圾收集的慢。自动生成的复制构造器是棺材中的一个钉子,用于隐藏新的和删除地狱的空洞。