为什么没有相当于 new/delete 系列的 realloc ?

问题描述

正如标题所说,我知道在 realloc/new 系列运算符中没有与 C 的 delete 等效的东西。

我已经找到了 this 问题,它稍微触及了主题,但并没有真正回答“为什么”。

我的问题是:

  1. 为什么能够重新分配对象是个坏主意?
  2. 为什么对象改变其大小是个坏主意? (实现一个集合似乎是一个让对象改变其大小的完全正当理由。)
  3. 在这些情况下会违反什么规则?为什么这条规则在客观上是好的?

解决方法

Realloc 有两种行为,其中一种行为在 C++ 对象模型中是不可接受的。 Realloc 可以增加一块存储的大小,也可以分配新的存储并将旧存储中的所有内容复制到新存储中。

问题是,C++ 并不认为对象只是一堆比特。它们是保持不变的活生生的呼吸类型。其中一些不变量不能容忍他们的位被很好地复制。

在 C++ 中,复制对象的位并不意味着您已经有效地复制了该对象。这仅适用于可简单复制的类型,并且有很多类型不可简单复制。

因此,C++ realloc 等效项不能用于任何分配。您需要将调用拆分为两个单独的调用:一个尝试扩展内存,如果不能,则什么都不做,以及您将使用现有 C++ 手动复制到的常规堆分配调用技术。


举个例子,许多 std::list 实现在 std::list 对象本身中存储了一个终止节点,用于表示链表的开始/结束。如果您只是简单地复制其位,则指向终止节点的指针将指向现已消失的旧分配。

这很糟糕。

为了允许对象具有访问这些类型的代码可以维护的任意类不变量,有必要将对象视为不仅仅是其对象表示的位。并且大多数 C++ 类型保持一些不变性,因此其对象表示无法在按位复制中幸存下来。

,

您不能在 C++ 中更改现有对象的存储。您唯一能做的就是在“重新分配的”内存中创建新对象,这些对象将具有与原始对象相同的内容。这正是 std::vector 能够做到的。

C++ 的一个问题是,此功能通常涉及的不仅仅是复制字节。通过复制对象的二进制表示来复制对象的内容仅适用于有限的类型集 — 所谓的 trivially-copyalbe 类型。对于其他的,需要涉及复制/移动构造函数析构函数

,

没有这样的功能,因为 C 库没有提供合适的接口来实现。 newdeletemallocfree 方面是可以实现的,但是假设的 renewrealloc 方面是不可实现的,因为你不能移动 C++ 对象的字节(其他人已经指出并解释了这个事实)。当 C++ 处于早期阶段时,这是一个重要的考虑因素,并且在某种程度上仍然如此。您通常不想编写自己的分配器(可能是次等的),因为您可以在非常成熟且经过验证的 malloc 和朋友的基础上捎带。

可以根据提供 try_realloc 之类的函数的低级分配器来实现 renew。检查块是否可以原位增长,如果是,则增长它,如果不能,则分配一个新块并将现有对象移动到它。双赢?显然,这个功能并不是很重要。 std::vector 将分配的大小加倍的事实上的政策工作得很好并且提供了非常好的性能,所以为什么要费心呢?

,

如果我们可以使用 stdlib.h 中的 C 实现,为什么我们必须有 new/delete 的等效项(粗略地说,这类似于严格类型的 malloc 和 free):)

char* str = new char[ 10 ];
strcpy( str,"hello wor" );
str = (char*)realloc( str,13 );
strcpy( str,"hello world" );
std::cout << str << std::endl;
delete str;