参见 new/delete 和 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
new。它以nothrow_t
作为第二个参数。以下形式的new-expression将调用只采用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;
}
如果分配函数返回存储空间,且由运行时创建的对象的构造函数抛出了异常,则自动调用operator delete。如果使用了带有其他参数的new形式,例如:
new (a, b, c) TypeId;
接着调用带有这些参数的operator delete。只有在对象的构造函数抛出异常时才会调用该operator delete版本进行删除。如果您自己调用delete,则编译器将使用仅接受void*
指针的普通operator delete函数:
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)
。new
与 malloc
的不同之处在于:
operator new
在分配的内存中构造一个值。这种行为可以通过重载此运算符来适应所有类型或仅适应您的类。总而言之,new
高度可定制,并且除了内存分配外还执行初始化工作。这是两个重要的区别。
虽然malloc
/free
和new
/delete
的行为不同,但从低层次上看,它们都是管理动态分配的内存。我猜这才是你真正想问的。在我的系统中,new
实际上内部调用了malloc
来执行其分配,所以我只会谈论malloc
。
malloc
和free
的实际实现可以有很多变化,因为有许多实现内存分配的方法。有些方法可以获得更好的性能,一些浪费较少的内存,其他方法则更适合调试。垃圾回收语言可能也有完全不同的分配方式,但你的问题涉及到C/C++。
sbrk
或 mmap
这样的系统调用。从堆中分配块的一种方法是维护一个自由块和已分配块的列表,该列表存储块大小和位置。最初,列表可能包含整个堆的一个大块。当请求新块时,分配器将从列表中选择一个自由块。如果块太大,则可以将其拆分为两个块(一个请求的大小,另一个是剩余大小)。当释放已分配块时,它可以与相邻的自由块合并,因为拥有一个大的自由块比拥有几个小的自由块更有用。块的实际列表可以存储为单独的数据结构或嵌入到堆中。
有很多变化。您可能希望保留免费和分配块的单独列表。如果您为常见大小的块分别拥有堆的不同区域或分别拥有这些大小的列表,则可能会获得更好的性能。例如,当您分配16字节块时,分配器可能具有特殊的16字节块列表,因此分配可以是O(1)。仅处理2的幂次方块大小也可能是有利的(其他任何内容都会四舍五入)。例如,Buddy allocator 就是这样工作的。
"new" 做的事情比 malloc 多得多。malloc 只是分配内存 - 它甚至不会为您清零。new 初始化对象,调用构造函数等。我认为在大多数实现中,对于基本类型,new 只是一个薄包装器,它背后还是使用了 malloc。