【发布时间】:2010-09-26 19:47:12
【问题描述】:
我知道它们在语法上有何不同,C++ 使用 new,而 C 使用 malloc。但是在高级解释中它们是如何工作的呢?
【问题讨论】:
-
我不认为这是重复的。我认为 Joel 是在询问 new/malloc 如何分配内存以及它们的实现方式是否存在差异。这与他们对用户的行为方式不同。
-
@Jay:查看 Joel 对@litb 回复的评论。
我知道它们在语法上有何不同,C++ 使用 new,而 C 使用 malloc。但是在高级解释中它们是如何工作的呢?
【问题讨论】:
我将引导您找到这个答案:What is the difference between new/delete and malloc/free?。马丁提供了一个很好的概述。关于它们如何工作的快速概览(无需深入探讨如何将它们作为成员函数重载):
示例:
new (a, b, c) TypeId;
// the function called by the compiler has to have the following signature:
operator new(std::size_t size, TypeOfA a, TypeOfB b, TypeOf C c);
new_handler,并希望它能够到位。如果仍然没有足够的地方,new 必须抛出std::bad_alloc 或从它派生。具有throw()(无抛出保证)的分配器,在这种情况下应返回一个空指针。 有一些特殊的分配函数被赋予了特殊的名称:
no-throw 新。这需要 nothrow_t 作为第二个参数。如下形式的新表达式将调用仅采用 std::size_t 和 nothrow_t 的分配函数:例子:
new (std::nothrow) TypeId;
placement new。这将 void* 指针作为第一个参数,而不是返回新分配的内存地址,而是返回该参数。它用于在给定地址创建对象。标准容器使用它来预分配空间,但仅在需要时才创建对象。代码:
// the following function is defined implicitly in the standard library
void * operator(std::size_t size, void * ptr) throw() {
return ptr;
}
如果分配函数返回存储,并且运行时创建的对象的构造函数抛出,则自动调用操作符delete。如果使用了带有附加参数的 new 形式,例如
new (a, b, c) TypeId;
然后调用带有这些参数的操作符 delete。该操作符删除版本仅在删除完成时才被调用,因为对象的构造函数确实抛出了。如果你自己调用 delete,那么编译器将使用普通的 operator delete 函数,只接受一个 void* 指针:
int * a = new int;
=> void * operator new(std::size_t size) throw(std::bad_alloc);
delete a;
=> void operator delete(void * ptr) throw();
TypeWhosCtorThrows * a = new ("argument") TypeWhosCtorThrows;
=> void * operator new(std::size_t size, char const* arg1) throw(std::bad_alloc);
=> void operator delete(void * ptr, char const* arg1) throw();
TypeWhosCtorDoesntThrow * a = new ("argument") TypeWhosCtorDoesntThrow;
=> void * operator new(std::size_t size, char const* arg1) throw(std::bad_alloc);
delete a;
=> void operator delete(void * ptr) throw();
如果你这样做了
new (possible_arguments) TypeId[N];
编译器使用operator new[] 函数而不是普通的operator new。运算符可以传递一个不完全是sizeof(TypeId)*N 的第一个参数:编译器可以添加一些空间来存储创建的对象的数量(必须能够调用析构函数)。标准是这样说的:
new T[5] 导致调用操作员 new[](sizeof(T)*5+x),并且new(2,f) T[5] 导致调用操作员 new[](sizeof(T)*5+y,2,f)。【讨论】:
虽然malloc/free 和new/delete 有不同的行为,但它们都在底层做同样的事情:管理动态分配的内存。我假设这是你真正要问的。在我的系统上,new实际上是在内部调用malloc来执行它的分配,所以我只讲malloc。
malloc 和free 的实际实现可能会有很大差异,因为实现内存分配的方法有很多。一些方法获得更好的性能,一些浪费更少的内存,另一些更适合调试。垃圾收集语言也可能有完全不同的分配方式,但你的问题是关于 C/C++。
通常,块是从堆中分配的,堆是程序地址空间中的一大块内存区域。该库为您管理堆,通常使用像sbrk 或mmap 这样的系统调用。从堆中分配块的一种方法是维护一个空闲和已分配块列表,其中存储块大小和位置。最初,列表可能包含整个堆的一个大块。当请求一个新块时,分配器将从列表中选择一个空闲块。如果块太大,可以将其分成两个块(一个是请求的大小,另一个是剩下的任何大小)。当一个分配的块被释放时,它可以与相邻的空闲块合并,因为拥有一个大的空闲块比几个小的空闲块更有用。实际的块列表可以存储为单独的数据结构或嵌入到堆中。
有很多变化。您可能希望保留单独的空闲和已分配块列表。如果您有单独的堆区域用于常见大小的块或单独的列表用于这些大小,您可能会获得更好的性能。例如,当您分配一个 16 字节块时,分配器可能有一个 16 字节块的特殊列表,因此分配可以是 O(1)。仅处理 2 的幂的块大小也可能是有利的(其他任何东西都被四舍五入)。例如,Buddy allocator 就是这样工作的。
【讨论】:
new 与 malloc 的不同之处如下:
operator new 在分配的内存中构造一个值。可以通过重载此运算符来调整此行为,可以适用于所有类型,也可以仅适用于您的类。总而言之,new 是高度可定制的,并且除了内存分配之外还进行初始化工作。这是两大区别。
【讨论】:
在 C 中: malloc 分配一块您在参数中提供的大小的内存,并返回一个指向该内存的指针。
内存是在堆上声明的,所以请确保在完成后释放它。
【讨论】:
"new" 比 malloc 做得更多。 malloc 只是分配内存 - 它甚至不会为您将其归零。 new 初始化对象,调用构造函数等。我怀疑在大多数实现中 new 只不过是基本类型的 malloc 的薄包装。
【讨论】: