为什么C++中存在delete[]语法?

每次有人delete[]在这里问一个问题时,总会有一个非常笼统delete[]的回答:“ C++ 就是这样做的,使用”。来自普通的 C 背景,我不明白为什么需要一个不同的调用。

使用malloc()/free()您的选择是获取指向连续内存块的指针并释放连续内存块。实现领域中的某些东西会根据基地址知道您分配的块的大小,以便您何时必须释放它。

没有功能free_array()。我在与此相关的其他问题上看到了一些疯狂的理论,例如调用delete ptr只会释放数组的顶部,而不是整个数组。或者更正确的是,它不是由实现定义的。当然……如果这是 C++ 的第一个版本,并且您做出了一个有意义的奇怪设计选择。但是为什么 with$PRESENT_YEAR的 C++ 标准没有被重载???

似乎 C++ 添加的唯一额外的一点是遍历数组并调用析构函数,我认为这可能是它的症结所在,它实际上是使用一个单独的函数来为我们节省单个运行时长度查找,或者nullptr在列表的末尾,以换取折磨每个新的 C++ 程序员或程序员,他们有一个模糊的一天并且忘记了有一个不同的保留字。

如果除了“这就是标准所说的并且没有人质疑它”之外还有其他原因,有人可以一劳永逸地澄清吗?

回答

C++ 中的对象通常具有需要在其生命周期结束时运行的析构函数。delete[]确保调用数组每个元素的析构函数。但是这样做有未指定的开销,而delete没有。一种用于数组,它支付开销,另一种用于不支付开销的单个对象。

为了只有一个版本,实现需要一种机制来跟踪有关每个指针的额外信息。但是 C++ 的创始原则之一是用户不应该被迫支付他们绝对不必支付的成本。

永远delete是你new,永远delete[]是你new[]。但在现代C ++,newnew[]一般不再使用。使用std::make_uniquestd::make_sharedstd::vector或其他更富有表现力和更安全的替代品。

  • Wow that was a quick response, thanks for the hints on allocation functions. It is surprising how often the answer in C++ is "don't use that keyword", use std::someWeirdFunctionIntroducedInC++>=11()
  • @awiebe You are correct that most of the time, if there is a neat standard library feature to replaces a built-in mechanism, it comes from C++11 or later. C++11 basically revolutionized the language, allowing for standard library features that were previously not possible to implement. The difference between C++11 and previous versions is so significant that they can basically be thought of as two different languages. Beware, when learning C++, to distinguish between education material targeting C++03 and earlier from material targeting C++11 and later.
  • @awiebe C++ gives you the tools to work as close to the hardware as it can. But those tools are usually powerful and blunt, making them dangerous and hard to use effectively. So it also provides tools via the standard library that are *slightly* farther removed from the hardware, but very safe and easy. This is why you learn about so many features, but are told not to use them. Because unless you are doing something very unique or strange, those low level tools are not useful. The more convenient features are generally just fine.
  • @awiebe, also note that the existence of lower level mechanisms like `new` allows most of the standard library (and other libraries) to be written in pure C++ (some parts may need compiler support). So the advice could also be "only use these to build higher level abstractions".
  • @FrançoisAndrieux: Choice-of-words-nitpick "...those tools are usually powerful and blunt...": I actually see them as super sharp, surgical tools: You can get exactly what you want, how you want it. But stitching up or cleaning up surgical procedures requires equivalent skills and materials, a band aid won't do.
  • Do note that sometimes you have to pair `new T()` with `delete[]`. "scalar-looking new" is perfectly capable of performing array allocation, if the actual type is an array.
  • @Barmar I reworded it to make it clearer that `delete[]` potentially has extra overhead related to destroying each array element.
  • @Barmar I think the concern was more about memory overhead rather than speed. The problem is made more apparent with placement new, where portable placement new of an array is currently not possible due to this potential overhead. See [Can placement new for arrays be used in a portable way?](https://stackoverflow.com/questions/15254/can-placement-new-for-arrays-be-used-in-a-portable-way). You may need to pass more storage than the elements would need, but it is not possible to ask the implementation how much extra storage it will need.
  • @dan04 The standard requires that a non-array new expression creating a `T` (`new T;`) is required to request exactly `sizeof(T)` bytes from the allocation function. Because of this rule, you can only implement `new T;` as `new T[1];` if new expression with an array type also has no array allocation overhead on that platform. [See](https://timsong-cpp.github.io/cppwp/expr.new#15) : *"That argument shall be no less than the size of the object being created; it may be greater than the size of the object being created only if the object is an array"*.
  • The answer is often "use some weird, probably-from-C++11 function", @awiebe, because a noticeable portion of the standard library is dedicated to providing wrappers around core language features to let the compiler do the heavy lifting for you whenever you don't need to optimise something to the absolute limit. It trades a potential slight drop in speed and/or increase in executable size for readable code, lessened bookkeeping on your end, and better guarantees of safety. For example, `std::vector` is essentially just a pretty wrapper that manages `new[]` and `delete[]` for you, with extras.

回答

基本上,mallocfree分配内存,并newdelete创建和销毁对象。所以你必须知道对象是什么。

要详细说明 François Andrieux 的回答提到的未指定开销,您可以查看我对这个问题的回答,其中我检查了特定实现的作用(Visual C++ 2013,32位)。其他实现可能会也可能不会做类似的事情。

如果new[]与具有非平凡析构函数的对象数组一起使用,它所做的是多分配 4 个字节,并返回前移 4 个字节的指针,因此当delete[]想知道那里有多少对象时,它需要指针,先将其移动 4 个字节,然后获取该地址处的数字并将其视为存储在那里的对象数。然后它在每个对象上调用一个析构函数(从传递的指针的类型知道对象的大小)。然后,为了释放确切的地址,它传递了传递地址之前 4 个字节的地址。

在这个实现中,将分配的数组传递new[]给常规会delete导致调用第一个元素的单个析构函数,然后将错误的地址传递给释放函数,从而破坏堆。不要这样做!


回答

东西不是在其他(都好)的答案中提到的是,这一现象的根本原因是阵列-由C继承-从来没有在C“一流”的事情++。

它们具有原始的 C 语义,没有 C++ 语义,因此没有 C++ 编译器和运行时支持,这将使您或编译器运行时系统使用指向它们的指针做有用的事情。

事实上,C++ 不支持它们,以至于指向一组事物的指针看起来就像指向单个事物的指针。特别是,如果数组是语言的适当部分,则不会发生这种情况——即使是作为库的一部分,如字符串或向量。

C++ 语言的这个问题是由于 C 的这种传统而发生的。它仍然是语言的一部分 - 即使我们现在有std::array固定长度的数组和(一直有)std::vector可变长度的数组 - 主要是为了兼容性:能够从 C++ 调用操作系统 API 和使用 C 语言互操作的其他语言编写的库。

而且……因为有大量书籍、网站和教室在他们的 C++ 教学法中很早就教授数组,因为 a) 能够在早期编写有用/有趣的示例,这些示例实际上调用了 OS API,当然因为 b) “这就是我们一直这样做的方式”的惊人力量。

  • Since a common uses of new[] is allocate an array of size unknown at compile time, C's pointer-to-array doesn't really help much anyway.
  • pointer-to-array decays instantly to pointer-to-element, though, and that's the way it's used., no? How many C++ (or C) function/method signatures take a pointer-to-array type? Nobody, but nobody, teaches that, nor is that how it is used. Do you disagree? E.g., show me where in Unix/Linux APIs a pointer-to-array is used in a function signature over a naked pointer assumed by documentation to be an array? @BenVoigt
  • Neither _Effective C++ - 3rd ed_ (Meyers, 2008) nor _More Effective C++_ (Meyers, 1996) mention pointer-to-array types. I could go on with books from my library, but ... I don't really care to. The point is not whether at some time - even originally - the languages, technically, had this ability. The point is that nobody has ever used it. Ever. The fact that I didn't mention it in my answer doesn't mean I didn't know of it. Just that I know it is a useless vestige of a compiler writer's store of knowledge. It has never been in use, never taught.
  • The core issue here is that pointer-to-array and reference-to-array types are _really_ hard to read, so people got into a habit of not using them, which leads to knowledge falling to the wayside. Easiest way to work with them is with templates or `decltype`, and using them normally quickly devolves into [a nigh-unreadable mess](https://www.ideone.com/nVDnRz). `create()` here is bad enough (in multiple ways), just imagine a function that takes pointers to two arrays and returns a pointer to a different kind of array.
  • This answer makes a number of utterly incorrect claims, evidently based on not knowing that both C and C++ support "pointer-to-array" types. It's not lack of ability to express a pointer to an array, it's disuse of that ability in practice.

回答

通常,C++ 编译器及其关联的运行时构建在平台的 C 运行时之上。特别是在这种情况下 C 内存管理器。

C 内存管理器允许您在不知道内存大小的情况下释放内存块,但是没有标准方法可以从运行时获取块的大小,并且无法保证实际分配的块大小与您的大小完全相同。要求。它可能更大。

因此,由 C 内存管理器存储的块大小不能有效地用于启用更高级别的功能。如果更高级别的功能需要有关分配大小的信息,那么它必须自己存储它。(delete[]对于带有析构函数的类型,C++确实需要它,以便为每个元素运行它们。)

C++ 也有一种“你只为你使用的东西付费”的态度,为每个分配存储一个额外的长度字段(与底层分配器的簿记分开)不适合这种态度。

由于在 C 和 C++ 中表示未知(在编译时)大小的数组的正常方法是使用指向其第一个元素的指针,因此编译器无法根据类型区分单个对象分配和数组分配系统。所以它留给程序员来区分。


以上是为什么C++中存在delete[]语法?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>