New (C++)
new是C++程序设计语言中的一种语言结构,用于动态分配内存、并用构造函数初始化分配的内存。 new的使用称为“new运算符表达式”,其内部实现分为两步:
每个new获取的对象,必须用delete析构并释放内存,以免内存泄漏。 new运算符表达式是C++的一种语言结构,不可重载。但用户可重载operator new()函数。 new运算符表达式语法普通的new运算符表达式new的语法是:
其中 通过
其中 动态生成对象数组的new运算符表达式
C++98标准规定,
如此生成的对象数组,在释放时必须调用 delete [] p_int ;
带位置的new运算符表达式带位置的new (placement new)的语法是:
其中,expression-list将作为operator new()函数的实参列表的结尾部分。这种形式的new运算符表达式首先调用operator new(size_t,OtherTypeList)函数来获取内存;然后对该对象执行构造函数。这里的OtherTypeList作为形参列表要和new表达式中第一个括号里的实参列表expression-list的类型兼容(即形参实参能够匹配)。 带位置的new运算符,语义上包括四种使用情形,其中前两种已经在标准头文件<new>中实现了:
狭义上的带位置的new是指第一种情形。使用这种placement new,原因之一是用户的程序不能在一块内存上自行调用其构造函数(即用户的程序不能显式调用构造函数),必须由编译系统生成的代码调用构造函数。原因之二是可能需要把对象放在特定硬件的内存地址上,或者放在多处理器内核的共享的内存地址上。 释放这种对象时,不能调用placement delete,应直接调用析构函数,如: 举例: #include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
char buf[100];
int *p=new (buf) int(101);
cout<<*(int*)buf<<endl;
return 0;
}
保证不抛出异常的new运算符表达式在分配内存失败时,new运算符的标准行为是抛出
或
其中 自行定制参数的new运算符表达式new运算符的参数可以是任意合法类型的列表。由C++的重载机制来决定调用那个operator new。
或
带位置的delete运算符表达式C++ 不能使用带位置的 delete 运算符表达式直接析构一个对象但不释放其内存。因此,对于用广义的带位置new表达式构建的对象,析构释放时有两种办法: 第一种办法是直接写一个函数,完成析构对象、释放内存的操作: void
destroy (T * p, A & arena)
{
p->~T() ; // First invoke the destructor explicitly.
arena.deallocate(p) ; // Then call the deallocator function directly.
}
如此使用: A arena ;
T * p = new (arena) T ;
/* ... */
destroy(p, arena) ;
第二种办法是分两步显式调用析构函数与带位置的operator delete函数: A arena ;
T * p = new (arena) T ;
/* ... */
p->~T() ; // First invoke the destructor explicitly.
operator delete(p, arena) ; // Then call the deallocator function indirectly via operator delete(void *, A &) .
带位置的operator delete()函数,可以被带位置的new算符表达式自动调用。这是在对象的构造函数抛出异常的时候,用来释放掉带位置的operator new函数获取的内存。以避免内存泄露。 例如:
operator new()的函数重载使用new动态生成一个对象,实际上是调用了new运算符表达式。该运算符首先调用了operator new函数动态分配内存,然后调用类型的构造函数初始化这块内存。new运算符是不能被重载的,但是下述各种operator new()函数既可以作为全局函数重载,也可以作为类成员函数或作用域内的函数重载,即由编程者指定如何获取内存。 普通的operator new(size_t size)函数new运算符调用operator new函数动态分配内存。首先查找类内是否有operator new函数可供使用(即依赖于实参的名字查找)。[1]operator new函数的参数是一个size_t类型,指明了需要分配内存的规模。[2]operator new函数可以被每个C++类作为成员函数重载。也可以作为全局函数重载: void * operator new (std::size_t) throw(std::bad_alloc);
void operator delete(void*) throw();
内存需要回收的话,调用对应的operator delete()函数。 例如,在new运算符表达式的第二步,调用构造函数初始化内存时如果抛出异常,异常处理机制在栈展开(stack unwinding)时,要回收在new运算符表达式的第一步已经动态分配到的内存,这时就会自动调用对应operator delete()函数。 数组形式的operator new[](size_t size)函数new Type[]运算符(array forms new),用来动态创建一个对象数组。这需要调用数组基类型内部定义的void* operator new[ ](size_t)函数来分配内存。如果数组基类型没有定义该函数,则调用全局的void* operator new[ ](size_t)函数来分配内存。 在<new>中声明了void* operator new[ ](size_t)全局函数: void * operator new[] (std::size_t) throw(std::bad_alloc);
void operator delete[](void*) throw();
void* operator new(size_t,void*)operator new(size_t,void*)函数用于带位置的new运算符调用。C++标准库已经提供了operator new(size_t,void*)函数的实现,包含<new>头文件即可。这个实现只是简单的把参数的指定的地址返回,带位置的new运算符就会在该地址上调用构造函数来初始化对象: // Default placement versions of operator new.
inline void* operator new(std::size_t, void* __p) throw() { return __p; }
inline void* operator new[](std::size_t, void* __p) throw() { return __p; }
// Default placement versions of operator delete.
inline void operator delete (void*, void*) throw() { }
inline void operator delete[](void*, void*) throw() { }
禁止重定义这4个函数。因为都已经作为<new>的内联函数了。在使用时,实际上不需要 对应的placement delete函数,只应在placement new运算符表达式在第二步调用构造函数抛出异常时被异常处理机制的栈展开操作自动调用。 保证不抛出异常的operator new函数C++标准库的<new>中还提供了一个nothrow的实现,用户可写自己的函数替代: void* operator new(std::size_t, const std::nothrow_t&) throw();
void* operator new[](std::size_t, const std::nothrow_t&) throw();
void operator delete(void*, const std::nothrow_t&) throw();
void operator delete[](void*, const std::nothrow_t&) throw();
自行定制参数的operator new函数这种函数被自行定制参数的new算符调用。需要由用户自行定义,以确定分配内存时的行为:
例如: char data[1000][sizeof(int)];
inline void* operator new(size_t ,int n)
{
return data[n];
}
void foo(){
int *p=new(6) int(102); //把整型对象创建在data的第六个单元上
}
参见参考文献
|
Portal di Ensiklopedia Dunia