为什么 CPP 不创建默认的深拷贝构造函数?

Why doesn't CPP create a default deep-copy constructor?

提问人:bilanush 提问时间:10/10/2019 更新时间:3/17/2020 访问量:980

问:

我不明白为什么他们没有一个复制构造函数来制作原始版本的真实倍数。

众所周知,默认复制构造函数的主要问题是,它执行的是浅层复制。因此,如果有指针,它只会复制它的地址,但为什么它不取消引用指针 a 复制内容? 当具有 dinamic 内存分配时会出现主要问题。这样一来,人们就可以错误地删除它,同时有一个指向它的指针,这就是为什么我们制作自己的复制构造函数并且不使用默认构造函数的原因。

但是我不明白,为什么CPP不做?为什么不复制内容

C++ 复制构造函数 default-constructor

评论

0赞 Paul Rooney 10/10/2019
如果您使用 vectors/unique_ptr 等,您通常可以避免手动内存管理,并避免实现 big 3
0赞 stark 10/10/2019
如果指针指向数组的中间怎么办?

答:

11赞 bolov 10/10/2019 #1

众所周知,默认复制构造函数的主要问题是,它执行的是浅层复制。

我们不知道。

这就是为什么我们制作自己的复制构造函数并且不使用默认构造函数的原因。

在 C++ 中,您几乎不应该编写自己的复制构造函数(零规则)。

当具有 dinamic 内存分配时会出现主要问题。这样人们就可以错误地删除它,同时有一个指向它的指针

这不是问题。为什么?因为在C++中,我们使用了RAII的概念,并且我们在标准库中提供了解决您看到的所有问题的工具。在 C++ 中,您永远不必编写显式,也不应该有一个作为所有者的原始指针。使用标准容器(例如)和智能指针(例如()。newstd::vectorstd::unique_ptr

我不明白为什么他们没有一个复制构造函数来制作原始版本的真实倍数

因为编译器不知道对象的复制语义应该是什么。只有班级的作者知道。你无法知道指针的语义是什么。是否是唯一拥有内存资源的指针?如果是这样,它是用 、 、 还是用其他东西获得的?它是否共享内存的所有权?或者它只是指向一个它不拥有的对象?由于您无法从类的声明/定义中了解其中任何内容,因此编译器根本无法使用原始指针自动实现“深度复制”。mallocnewnew[]

除非它确实如此。默认情况下,它确实实现了深拷贝,或者默认实现了浅拷贝,或者两者的组合。它这样做是正确的。还记得我告诉过你不要使用原始指针作为所有权吗?使用适当的抽象(容器、智能指针),默认的复制 ctor 将完全执行它需要做的事情。

评论

0赞 bilanush 10/10/2019
我不明白。你有一个指针,编译器有指针的内容,这是一个地址,只需获取该地址中的内容并复制它即可。为什么你不能这样做。
0赞 bilanush 10/10/2019
至于你回答的第一部分,这就是我在互联网上看到的原因。为什么我们应该制作自己的复制构造函数
0赞 bolov 10/10/2019
@bilanush在哪里复制它?您正在创建的副本包含一个新指针。此指针应指向何处?到旧地址 - 那是浅层副本。到新地址?什么新地址?您如何在这个新地址分配空间?
0赞 bilanush 10/10/2019
只需分配另一个空格并创建一个指针来包含该新地址
2赞 bolov 10/10/2019
@bilanush,网上还有很多关于C++的糟糕和非常糟糕的建议。要非常小心。使用一本教授现代C++的好书。您永远不应该拥有手动管理的动态分配内存(除非您正在编写库)
4赞 eerorika 10/10/2019 #2

众所周知,默认复制构造函数的主要问题是,它执行的是浅层复制。

一般来说,浅拷贝不是问题。仅当要创建深层复制、具有引用成员并假定隐式复制构造函数来执行所需的操作时,这才是一个问题。

浅层复制通常是有用的,而且通常是故意的。

但是,为什么它不取消引用指针 A 复制内容呢?

因为复制构造函数将在哪里存储复制的对象?指针成员应指向何处?

编译器无法读懂您的心思,也无法知道您是要分配内存,还是如何分配内存。如果编译器确实在隐式函数中分配了内存,那么谁将负责删除它?如果编译器隐式删除了任何指针,那么当您打算使用指向不得删除的内容的非所有权指针时,这将是非常令人惊讶的。

为什么程序员不能对此负责?

内存管理已经足够困难了。目前,它至少可以通过遵循简单的规则进行管理:的一切。如果我们引入隐式分配,并让程序员有责任知道这些隐式分配的存在,我们的工作将变得更加困难。deletenew

此外,指针可以具有无效值。在这种情况下,通过它进行间接操作将具有未定义的行为。并且无法检查指针以找出它是否无效。这将使建议的隐式“深度复制”极易出错。

浅层隐式复制是具有手动内存管理的语言中唯一明智的选择。即使在垃圾回收语言中,这通常也是更好的选择。

这就是为什么我们制作自己的复制构造函数并且不使用默认构造函数的原因。

我们很少编写用户声明的复制构造函数(初学者课程之外)。这些主要是用于管理内存(或其他资源)的类,例如智能指针。我们很少需要编写这些内容,因为标准库提供了最通用的智能指针和开箱即用的数据结构。

一旦我们在类中拥有动态内存,我们就应该创建自己的复制构造函数

事实上,如果你的类管理动态内存,那么它将需要一个自定义的复制构造函数。但典型的解决方案是不要管理类内的动态内存。见上文。将所有内存分配和其他动态资源保存在智能指针或容器中。

评论

0赞 bilanush 10/10/2019
我在 geeksforgeeks 中看到,一旦我们在类中拥有动态内存,我们就应该创建自己的复制构造函数,例如使用 new 关键字声明的指针。我唯一不明白的是,为什么 CPP 不能创建一个副本来分配内存并取消引用指针以获取内容并复制它。你说它不能做,因为“那么谁来负责它的删除?但是我不明白为什么程序员不能对此负责?他复制了它,所以他确切地知道,无论我们在这里拥有什么指针+分配,我们都将在另一个对象中拥有
0赞 user4581301 10/10/2019
两件事。1. 它远远超出了记忆。任何资源都应得到充分保护。2. 编译器不知道你要用指针做什么。计算机还不够聪明,无法识别上下文,坦率地说,人类在很多时候也不是。
1赞 eerorika 10/10/2019
@bilanush在我的编辑中看到添加的内容。如果你仍然不相信,请随意使用隐式深度复制创建自己的语言,而不会牺牲 C++ 的优势,并证明我错了。
1赞 Andreas Wenzel 10/10/2019 #3

The compiler has no way of knowing the meaning of the pointers it is supposed to "deep copy".

For example, does a float pointer point to a single float or to a C-style float array? If it is an array, what is the length of the array that it should copy? Please note that I am not talking about C++ style arrays (i.e. ).std::array

If you want "deep copying" to be handled automatically, you can use container classes for data members that should be copied.

评论

0赞 bilanush 10/10/2019
Why can't it dereference the content and get its copy placed somewhere else? It isn't possible for the CPP to dereference the content of an address? It doesn't know it's size per bytes?
0赞 user4581301 10/10/2019
@bilanush what if the pointer's not valid yet or has been rendered invalid by some other operation?
0赞 bilanush 10/10/2019
I don't understand. So what? You have it's address right? Simply get its content and multiply it. What do you mean by invalid? If the content is not anymore then you would probably not get any content by dereferencing it so its good
0赞 Andreas Wenzel 10/10/2019
C++ style arrays (std::array) are aware of their own size, however this is not true for pointers to C style arrays. Such a pointer simply points to the first element of the array, without knowing its size. It is the responsibility of the programmer to know the C style array's size and to not access the array past its boundary. Therefore, only the first element of the array can (normally) be safely dereferenced if you don't know the array's size, provided the pointer to the start of the array is not NULL. However, there are exceptions to this.
0赞 Andreas Wenzel 10/10/2019
@bilanush: Dereferencing an invalid pointer will result in undefined behavior, i.e. your program will most likely crash.