【问题标题】:Why doesn't CPP create a default deep-copy constructor?为什么 CPP 不创建默认的深拷贝构造函数?
【发布时间】:2020-02-07 09:26:12
【问题描述】:

我不明白为什么他们没有一个复制构造函数,它使原始的倍数成为真正的倍数。

我们知道默认拷贝构造函数的主要问题是,它执行的是浅拷贝。因此,如果有一个指针,它只会复制它的地址,但为什么不取消引用指针 a 复制内容? 主要问题出现在动态分配内存时。这样一来,当有一个指向它的指针时,可能会错误地删除它,这就是我们制作自己的复制构造函数而不使用默认构造函数的原因。

但我无法得到它,为什么CPP不做呢?为什么不复制内容

【问题讨论】:

  • 如果你使用vectors/unique_ptr等你通常可以避免手动内存管理,避免需要实现the big 3
  • 指针指向数组中间怎么办?

标签: c++ copy-constructor default-constructor


【解决方案1】:

我们知道默认复制构造函数的主要问题是,它执行的是浅拷贝。

我们不知道。

这就是我们制作自己的复制构造函数而不使用默认构造函数的原因。

在 C++ 中,您几乎不应该编写自己的复制构造函数 (the rule of zero)。

主要问题出现在动态分配内存时。这样就可以在有一个指向它的指针时错误地删除它

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

我不明白为什么他们没有一个复制构造函数,它使原始的倍数成为真正的倍数

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

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

【讨论】:

  • 我不明白。你有一个指针,编译器有指针的内容,它是一个地址,只需获取该地址中的内容并复制它。为什么你做不到。
  • 关于你回答的第一部分,这些是我在网上看到的原因。为什么我们应该创建自己的复制构造函数
  • @bilanush 复制到哪里?您正在创建的副本包含一个新指针。这个指针应该指向哪里?到旧地址 - 那是浅拷贝。到新地址?什么新地址?您如何在这个新地址分配空间?
  • 只需分配另一个空间并创建一个指针来包含该新地址
  • @bilanush 同样,网上有很多关于 C++ 的坏和非常坏的建议。要非常小心。使用一本讲授现代 C++ 的好书。您永远不应该拥有手动管理的动态分配内存(除非您正在编写库)
【解决方案2】:

我们知道默认复制构造函数的主要问题是,它执行的是浅拷贝。

一般来说,浅拷贝不是问题。如果您想进行深层复制、拥有引用成员并假设隐式复制构造函数执行您想要的操作,这只是一个问题。

浅拷贝通常很有用,而且通常是故意的。

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

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

编译器无法读懂你的想法,也不知道你是否要分配内存,或者如何分配内存。如果编译器确实在隐式函数中分配了内存,那么谁将负责删除它?如果编译器隐式删除了任何指针,那么当您打算拥有指向不能删除的东西的非拥有指针时,这将是非常令人惊讶的。

为什么程序员不能负责?

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

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

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

这就是我们制作自己的复制构造函数而不使用默认构造函数的原因。

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

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

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

【讨论】:

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

编译器无法知道它应该“深拷贝”的指针的含义。

例如,浮点指针是指向单个浮点数还是指向 C 样式的浮点数数组?如果它是一个数组,它应该复制的数组的长度是多少?请注意,我不是在谈论 C++ 样式数组(即std::array)。

如果您希望自动处理“深度复制”,您可以将container classes 用于应复制的数据成员。

【讨论】:

  • 为什么它不能取消引用内容并将其副本放在其他地方? CPP 不可能取消引用地址的内容吗?它不知道每字节的大小?
  • @bilanush 如果指针无效或已被其他操作无效怎么办?
  • 我不明白。所以呢?你有它的地址吗?只需获取其内容并将其相乘即可。你说的无效是什么意思?如果内容不再存在,那么您可能无法通过取消引用来获得任何内容,所以它很好
  • C++ 样式数组 (std::array) 知道它们自己的大小,但是对于指向 C 样式数组的指针,情况并非如此。这样的指针只是指向数组的第一个元素,而不知道它的大小。程序员有责任了解 C 样式数组的大小并且不访问超出其边界的数组。因此,如果您不知道数组的大小,只要指向数组开头的指针不为 NULL,就可以(通常)安全地取消引用数组的第一个元素。但是,也有例外。
  • @bilanush:取消引用无效指针将导致undefined behavior,即您的程序很可能会崩溃。
猜你喜欢
  • 2018-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-11
  • 1970-01-01
  • 2013-03-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多