我不明白为什么他们没有一个复制构造函数,该复制构造函数是原始副本的真正倍数。
我们知道默认拷贝构造函数的主要问题是执行浅拷贝。这样,如果有一个指针,它只会复制它的地址,但是为什么不取消引用该指针而复制内容呢? 当动态分配内存时会出现主要问题。这样一来,当有人指向它时,可能会错误地删除它,这就是为什么我们制作自己的副本构造函数而不使用默认的构造函数的原因。
但是我不明白,为什么CPP不这样做?为什么不复制内容
答案 0 :(得分:9)
我们知道默认拷贝构造函数的主要问题是执行浅拷贝。
我们不知道。
这就是为什么我们要创建自己的副本构造函数而不使用默认构造函数的原因。
在C ++中,几乎应该永远不要编写自己的副本构造函数(the rule of zero)。
当动态分配内存时会出现主要问题。这样一来,在指针指向它的情况下可以错误地删除它
这不是问题。为什么?因为在C ++中,我们使用RAII的概念,并且标准库中有一些工具可以解决您看到的所有问题。在C ++中,您永远不必编写显式的new
,并且永远不要拥有作为拥有者的原始指针。使用标准容器(例如std::vector
)和智能指针,例如(std::unique_ptr
。
我不明白为什么他们没有一个复制构造函数,该复制构造函数是原始副本的真正倍数
因为编译器不知道对象的复制语义应该是什么。只有全班作家才知道。您不知道指针的语义是什么。是唯一拥有内存资源的指针吗?如果是这样,是通过malloc
,new
,new[]
还是其他东西获得的?它是否共享其对内存的所有权?还是只是指向它不拥有的对象?由于您无法从类的声明/定义中了解到这些信息,因此编译器根本无法使用原始指针自动实现“深层复制”。
除了可以。默认情况下,它确实实现了深拷贝,或默认情况下实现了浅拷贝,或它们的组合。而且这样做正确。还记得我告诉过您不要将原始指针用于所有权吗?使用适当的抽象(容器,智能指针),默认的复制ctor将完全执行其所需的工作。
答案 1 :(得分:4)
我们知道默认拷贝构造函数的主要问题是执行浅拷贝。
通常,浅复制不是问题。如果您想进行深拷贝,拥有一个引用成员,并假定隐式拷贝构造函数可以执行您想要的操作,那么这只是一个问题。
浅度复制通常很有用,而且通常是有意复制的。
但是为什么不取消引用指针a来复制内容呢?
因为复制构造函数会将复制的对象存储在哪里?指针成员应该指向哪里?
编译器无法理解您的想法,也无法知道您是要分配内存还是如何分配内存。如果编译器确实在隐式函数中分配了内存,那么谁来负责删除它呢?如果编译器隐式删除了任何指针,那么当您打算拥有一个不能删除的东西的非所有者指针时,这将非常令人惊讶。
为什么程序员不对此负责?
内存管理非常困难。目前,至少可以通过以下简单规则进行管理:您 delete
您 new
的所有内容。如果我们引入隐式分配,并让程序员有责任知道这些隐式分配的存在,那么我们的工作将变得更加困难。
此外,指针可以具有无效值。在这种情况下,通过它间接进行将具有不确定的行为。并且不可能检查指针以找出其是否无效。这将使建议的隐式“深度复制”易于出错。
在具有手动内存管理的语言中,浅层隐式复制是唯一明智的选择。即使是用垃圾收集的语言,这通常也是更好的选择。
这就是为什么我们要创建自己的副本构造函数而不使用默认构造函数的原因。
我们很少编写用户声明的副本构造函数(在初学者课程之外)。对于那些其唯一目的是管理内存(或其他资源)(例如智能指针)的类,大多数需要它们。而且我们几乎不需要编写这些代码,因为标准库提供了现成的最通用的智能指针和数据结构。
一旦我们在类中拥有动态内存,就应该创建自己的副本构造函数
的确,如果您的类管理动态内存,那么它将需要一个自定义的副本构造函数。但是典型的解决方案是不管理类内部的动态内存。参见上面的段落。将所有内存分配和其他动态资源保存在智能指针或容器中。
答案 2 :(得分:1)
编译器无法知道应该进行“深度复制”的指针的含义。
例如,浮点型指针是否指向单个浮点型或C样式的浮点型数组?如果是数组,应复制的数组长度是多少?请注意,我不是在谈论C ++样式数组(即std :: array)。
如果要自动处理“深层复制”,可以对要复制的数据成员使用container classes。