【发布时间】:2021-03-28 14:39:45
【问题描述】:
我知道 C++ 中的 free 和 delete 之间的区别。但我从来不明白的一件事是,为什么在 C 中 malloc/free 可以分配取消分配单个“对象”和数组,但在 C++ 中,我们需要使用正确的 new/delete vs new[]/delete[] 对。
在 Stackoverflow 上搜索,似乎在 C++ 中,new[] 分配了额外的内存来保存已分配数组的大小,而 new 仅将内存分配给对象本身。因此,您应该注意这种额外的开销。
如果上一段确实如此,那么 malloc/free 如何处理这个开销?或者他们只是接受这个开销?如果它在 C 中是可以容忍的,为什么不能在 C++ 中呢?
另一方面,如果不是因为内存开销,而是因为调用构造函数和析构函数,编译器就不能足够聪明地在后台生成适当的代码,让程序员只写 new/delete对于单个对象和对象数组?
我正在为一种语义类似于 C++ 的玩具语言编写编译器,似乎可以让编译器决定如何仅使用 new/delete 进行分配和取消分配,但由于 C++ 使用 new/delete和 new[]/delete[],也许有一个我现在没有看到的问题。也许与多态性和虚拟表有关?
如果您好奇,我的幼稚想法是简单地分配一个整数以及对象/数组,其中该整数是数组的大小,如果是对象,则为简单的 1。然后在调用delete的时候检查整数的值,如果是1就调用析构函数。如果它大于 1,则将调用析构函数的数组迭代到数组中的每个对象。正如我所说,它似乎有效,并且会让程序员只写 new/delete 而不是 new/delete vs new[]/delete。但话又说回来,也许有一个我没有看到的问题。
编辑部分:
经过一些回答,我决定尝试提供一些伪代码和更好的背景。
在 C 语言中,内存分配通常使用 malloc() 进行,而取消分配则使用 free()。如果您要分配单个 POD、单个结构或数组, malloc() 适合所有这些情况。如果要分配单个结构,则不需要 malloc() 的不同版本,而如果要分配数组,则不需要 malloc_array() 版本。至少在公共 API 级别。换句话说,似乎分配几个字节或多个字节都没有关系,记录分配大小信息不会有任何开销。
正如你们中的许多人(包括我自己)所知道的,new 和 delete 不仅仅是分配和取消分配内存。 new 分配内存并调用构造函数,delete 调用析构函数然后释放内存。但是在 C++ 中,您需要注意分配的只是单个对象还是对象数组。如果要分配数组,则需要使用 new[]/delete[] 对。
在 C 中,如果您实现二叉树,节点将使用 malloc 分配并使用 free 取消分配,而在 C++ 中使用 new 和 delete。但是,如果你在 C++ 中实现类似向量类的东西,在 C 中你仍然会使用 malloc/free,但现在在 C++ 中你需要使用 new[]/delete[](考虑一个没有太多黑魔法的理智实现) .
考虑以下由编译器执行的伪代码。在这个伪代码中,delete 函数以某种方式访问 malloc 内部并知道有多少字节,这反过来可以很容易地用于计算有多少对象。由于此删除实现使用 malloc 内部来了解分配了多少内存,因此理论上不应该有记账开销。
// ClassType is a meta type only know by the compiler
// it stores a class info such as name, size, constructors and so on
void *new(ClassType c) {
// allocates memory with malloc. Malloc() do the storage bookkeeping
// note that somehow malloc is allocating just a single object
c *ptr = malloc(sizeof(c));
// now, call the constructor of the requested class
c.constructor(ptr);
// return the new object
return ptr;
}
void *new(ClassType c, size_t n) {
c *ptr = malloc(sizeof(c) * n);
// iterate over the array and construct each object
for (i = 0; i < n; ++i) {
c.constructor(ptr[i]);
}
return ptr;
}
// this delete version seems to be able to de-allocate both single
// objects and arrays of objects with no overhead of bookkeeping because
// the bookkeeping is made by malloc/free. So I would need
// just a new/delete pair instead of new/delete vs new[]/delete[]
// Why C++ doesn't use something like my proposed implementation?
// What low-level details prohibits this implementation from working?
void delete(ClassType c, void *ptr) {
// get raw information of how many bytes are used by ptr;
size_t n = malloc_internals_get_size(ptr);
// convert the number of bytes to number of objects in the array
n = c.bytesToClassSize(n);
c* castedPointer = (c*) ptr;
// calls the destructor
for (i = 0; i < n; ++i) {
c.destructor(castedPointer[i]);
}
// free memory chunk
free(ptr);
}
【问题讨论】:
-
free 不会调用析构函数,delete 会。如果析构函数中存在副作用,则基本上是在添加一个很难找到的错误。
-
如果您查看
malloc声明,它接受以字节为单位的大小并返回void *类型的未初始化内存块,free稍后发布。相反,new构造对象,delete破坏它们,因此它需要知道它应该作用于数组的每个元素。它可以是数组感知的,但他们选择了这样的样板方法,我不知道为什么。 -
我知道这一点,但它没有解释为什么 free 可以同时处理单个“对象”和数组,但在 C++ 中我们需要删除/删除 []。如果我的问题对此主题不清楚,请帮助我改进它
-
因此,您应该意识到这种额外的开销。
new可能会做很多事情。malloc也可以。或malloc下方的任何内容。你可以请求 2 个字节并获得 4K 分配,如果这是内存源可以提供的。 -
你的“天真的想法”实现了
delete[]。要实现delete,根本不需要该整数,因此开销较小
标签: c++ arrays memory-management compiler-construction dynamic-memory-allocation