Loading AI tools
来自维基百科,自由的百科全书
new是C++程式设计语言中的一种语言结构,用于动态分配内存、并用构造函数初始化分配的内存。
new的使用称为“new运算符表达式”,其内部实现分为两步:
std::bad_alloc
异常。“new运算符表达式”所调用的operator new()函数,按照C++的名字查找规则,首先做依赖于实参的名字查找(即ADL规则),在要申请内存的数据类型T的内部、数据类型T定义处的命名空间查找;如果没有查找到,则直接调用全局的::operator new()函数。每个new获取的对象,必须用delete析构并释放内存,以免内存泄漏。
new运算符表达式是C++的一种语言结构,不可重载。但用户可重载operator new()函数。
new的语法是:
p_var = new typename;
其中p_var
是已经定义的类型为typename
的指针变量。
通过new
初始化对象,使用下述语法:
p_var = new type(initializer); // 或者如此初始化 new type{initializer};
其中initializer
是传递给构造函数的实参表或初值。
new
也可创建一个对象数组,称之为“array forms new”:
p_var = new type [size];
C++98标准规定,new
创建的对象数组不能被显式初始化, 数组所有元素被缺省初始化。如果数组基类型没有缺省初始化,则编译报错。但C++11已经允许显式初始化,例如:
int *p_int = new int[3] {1,2,3};
如此生成的对象数组,在释放时必须调用delete [ ]
表达式。例如
delete [] p_int ;
带位置的new (placement new)的语法是:
new ( expression-list ) new-type-id ( optional-initializer-expression-list );
其中,expression-list将作为operator new()函数的实参列表的结尾部分。这种形式的new运算符表达式首先调用operator new(size_t,OtherTypeList)函数来获取内存;然后对该对象执行构造函数。这里的OtherTypeList作为形参列表要和new表达式中第一个括号里的实参列表expression-list的类型兼容(即形参实参能够匹配)。
带位置的new运算符,语义上包括四种使用情形,其中前两种已经在标准头文件<new>中实现了:
狭义上的带位置的new是指第一种情形。使用这种placement new,原因之一是用户的程序不能在一块内存上自行调用其构造函数(即用户的程序不能显式调用构造函数),必须由编译系统生成的代码调用构造函数。原因之二是可能需要把对象放在特定硬件的内存地址上,或者放在多处理器内核的共享的内存地址上。
释放这种对象时,不能调用placement delete,应直接调用析构函数,如:pObj->~ClassType();
然后再自行释放内存。
举例:
#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运算符的标准行为是抛出std::bad_alloc
异常。也可以让new运算符在分配内存失败时不抛出异常而是返回空指针。
new (nothrow) Type ( optional-initializer-expression-list );
或
new (nothrow) Type[size];
其中nothrow
是std::nothrow_t
的一个实例
new运算符的参数可以是任意合法类型的列表。由C++的重载机制来决定调用那个operator new。
new (Type_list) Type ( optional-initializer-expression-list );
或
new (Type_list) Type[size];
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函数获取的内存。以避免内存泄露。
例如:
#include <cstdlib>
#include <iostream>
char buf[100];
struct A {} ;
struct E {} ;
class T {
public:
T() { throw E() ; }
void * operator new(std::size_t,const A &){
std::cout << "Placement new called for class T." << std::endl;
return buf;
}
void operator delete(void*, const A &)
{
std::cout << "Placement delete called for class T." << std::endl;
}
} ;
void * operator new ( std::size_t, const A & )
{std::cout << "Placement new called." << std::endl; return buf;}
void operator delete ( void *, const A & )
{std::cout << "Placement delete called." << std::endl;}
int main ()
{
A a ;
try {
T * p = new (a) T ;
/* do something */
} catch (E exp) {std::cout << "Exception caught." << std::endl;}
return 0 ;
}
使用new动态生成一个对象,实际上是调用了new运算符表达式。该运算符首先调用了operator new函数动态分配内存,然后调用类型的构造函数初始化这块内存。new运算符是不能被重载的,但是下述各种operator new()函数既可以作为全局函数重载,也可以作为类成员函数或作用域内的函数重载,即由编程者指定如何获取内存。
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()函数。
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();
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>的内联函数了。在使用时,实际上不需要#include <new>
对应的placement delete函数,只应在placement 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();
这种函数被自行定制参数的new算符调用。需要由用户自行定义,以确定分配内存时的行为:
operator new(size_,Type1, Type2, ... );
例如:
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的第六个单元上
}
Seamless Wikipedia browsing. On steroids.
Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.
Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.