C++ 中的new运算符用法介绍

new是C++程序设计语言中的一种语言结构,用于动态分配内存、并用构造函数初始化分配的内存。

new的使用称为“new运算符表达式”,其内部实现分为两步:

调用相应的operator new()函数,动态分配内存。如果operator new()不能成功获得内存,则调用new_handler函数。如果没有设置new_handler函数或者new_handler未能分配足够内存,则抛出std::bad_alloc异常。“new运算符表达式”所调用的operator new()函数,按照C++的名字查找规则,首先做依赖于实参的名字查找(即ADL规则),在要申请内存的数据类型T的内部、数据类型T定义处的命名空间查找;如果没有查找到,则直接调用全局的::operator new()函数。

在分配到的动态内存块上初始化相应类型的对象并返回其首地址。如果调用构造函数初始化对象时抛出异常,则自动调用operator delete()函数释放已经分配到的内存。

每个new获取的对象,必须用delete析构并释放内存,以免内存泄漏。

new运算符表达式是C++的一种语言结构,不可重载。但用户可重载operator new()函数。

2.new运算符表达式

2.1 普通的new运算符

model:

p = new typename;
p = new typename(init);
p = new typename{init};
```

eg:

```c++
int *p = new int;
int *p = new int(5);
int *p = new int{5};//C++ 11
2.2 生成对象数组的new运算符

model:

p = new type[size];
p = new type[size] {one, two , size};
eg:

p = new int[5];
p = new int[5]{1, 2, 3, 4, 5};
2.3 带位置的new运算符

model:

new ( expression-list ) type ( init);
expression-list将作为 operator new() 函数的实参列表的结尾部分。这种形式的new运算符表达式首先调用 operator new(size_t,OtherTypeList) 函数来获取内存;然后对该对象执行构造函数。这里的OtherTypeList作为形参列表要和new括号里的实参列表expression-list的类型兼容(即形参实参能够匹配)。

带位置的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;
}

2.4 保证不抛出异常的new 运算符

在分配内存失败时,new运算符的标准行为是抛出std::bad_alloc异常。也可以让new运算符在分配内存失败时不抛出异常而是返回空指针。其中 nothrow 是 std::nothrow_t 的一个实例.

p = new (nothrow) type(init);
p = new (nothrow) type[size];

2.5 自行定制参数的new运算符表达式

new运算符的参数可以是任意合法类型的列表。由C++的重载机制来决定调用那个operator new。

new (Type_list) Type ( init );
new (Type_list) Type[size];

2.6 带位置的delete运算符

C++ 不能使用带位置的 delete 运算符表达式直接析构一个对象但不释放其内存。因此,对于用广义的带位置new表达式构建的对象,析构释放时有两种办法:

第一种办法是直接写一个函数,完成析构对象、释放内存的操作 。

3.operator new() 函数重载

3.1普通的operator new(size_t size)函数

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()函数。

3.2数组形式的operator new [](size_t size)函数

void * operator new[] (std::size_t) throw(std::bad_alloc);
void operator delete[](void*) throw();

3.3 带位置的operator new函数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> 的内联函数了。在使用时,实际上不需要 #include <new>

对应的placement delete函数,只应在placement new运算符表达式在第二步调用构造函数抛出异常时被异常处理机制的栈展开操作自动调用。

3.5保证不抛出异常的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();

3.6自行定制参数的operator new函数

这种函数被自行定制参数的new算符调用。需要由用户自行定义,以确定分配内存时的行为.

operator new(size_,Type1, Type2, ... );
eg:

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的第六个单元上
}

placement new 放置new

>void*operator new(std::size_t ,void *);  
void operator delete( void * ,void *); 该运算符是在已分配的内存上重新构造对象,因为不分配内存,所以不必担心分配失败。唯一的工作是调用构造函数。要包含 <new>头文件。

# include <new>  
# include <iostream>  
void main()  
{  using namespace std;  
    char * p = new(nothrow) char [4];  
    if (p == NULL)  
    {  cout < <“allocte failed” < <endl;  exit( -1 );    }  
    // ...  
    long * q = new(p)long(1000);  
    delete [ ]p;    //只释放 p,不要用q释放。  
}  p和q仅仅是首址相同,所构建的对象可以类型不同。所“放置”的空间应小于原空间,以防不测。当”放置new”超过了申请的范围,Debug版下会挂机,但Release版竟然能运行而不出错!
该运算符的作用是:只要第一次分配成功,不再担心分配失败。

# include <new>  
# include <iostream>  
void main()  
{  using namespace std;  
    char * p = new(nothrow) char [100];  
    if (p == NULL)  
    {  cout < <“allocte failed” < <endl;  exit( -1 );    }  
    long * q1 = new(p)long(100);  
    // 使用q1  ...  
    int * q2 = new(p) int[100/sizeof(int) ];  
    // 使用q2 ...  
    ADT * q3 = new(p) ADT[100/sizeof(ADT) ];  
    // 使用q3  然后释放对象 ...  
    delete [ ]p;    //只释放空间,不再析构对象。  
}注意:使用该运算符构造的对象或数组,一定要显式调用析构函数,不可用delete代替析构,因为placement new 的对象的大小不再与原空间相同。
# include <new>  
# include <iostream>  
void main()  
{  using namespace std;  
    char * p = new(nothrow) char [sizeof(ADT)+2];  
    if (p == NULL)  
    {  cout < <“allocte failed” < <endl;  exit( -1 );    }  
    // ...  
    ADT * q = new(p) ADT;  
    // ...  
    // delete q;    // 错误  
    q-> ADT::~ADT(); //显式调用析构函数,仅释放对象  
    delete [ ]p;    //最后,再用原指针来释放内存.  
}  placement new 的主要用途就是可以反复使用一块已申请成功的内存空间。这样可以避免申请失败的徒劳,又可以避免使用后的释放。

    特别要注意的是对于 placement new 绝不可以调用的delete, 因为该new只是使用别人替它申请的地方(只是个租房户,不是房主。无权将房子卖掉)。释放内存是nothrow new的事,即要使用原来的指针释放内存

时间: 2024-09-26 07:29:44

C++ 中的new运算符用法介绍的相关文章

Excel2007中网格线的基本用法介绍

Excel2007中网格线的基本用法介绍 Excel2007中的网格线在编辑.打印操作中都会用到.下面讲解Excel2007软件中网格线的一些基本用法. 1.隐藏/显示网格线 通过"视图"选项卡菜单中的"网格线"复选框开关,你可以设置网格线在编辑表时候的显示与否,   2.打印输出网格线设置 Excel中默认的网格线在打印时候是不会输出的,除非你进行了单元格的边框设置.设置方法:"开始"选项卡菜单中"字体"栏的"边框

Python中的with...as用法介绍

  这篇文章主要介绍了Python中的with...as用法介绍,本文直接给出用法实例,需要的朋友可以参考下 这个语法是用来代替传统的try...finally语法的. 代码如下: with EXPRESSION [ as VARIABLE] WITH-BLOCK 基本思想是with所求值的对象必须有一个__enter__()方法,一个__exit__()方法. 紧跟with后面的语句被求值后,返回对象的__enter__()方法被调用,这个方法的返回值将被赋值给as后面的变量.当with后面的

js 与或运算符 || &amp;&amp; 用法介绍

1.逻辑或运算符||: 由于&&和||可能不考虑第二个运算数,所以我们应尽量避免在它们右边使用具有副作用(赋值.递增.递减和函数调用)的表达式,除非非常清楚自己再做什么.  代码如下 复制代码 if((a == null) && (b++ >10)) stop(); //b++递增运算可能不被执行 if((b++ >10) && (a == null)) stop(); //保证b++递增运算都被执行 2.逐位运算 &和|除了可以进行&q

js中的布尔运算符使用介绍_javascript技巧

在之前我们讨论到布尔运算符 && 和 || 的时候,我说过它们的结果是布尔值.这样说起来有点过于简单化了.如果你用它们来计算布尔数据类型,它们确实会返回布尔值.但是它们也能用于计算其他的类型的数据,这种时候,返回的就将是其中的一个参数了. 或运算符"||" 真正做的事是这样的:它首先检查一下它左边的参数,如果转换成布尔类型的值后为true,那么就返回左边的参数,否则就返回右边的参数. 仔细想想当运算符两边都是布尔类型的值的时候,是不是这样的.为什么它这样运行?这样运行的

PHP中extract函数各种用法介绍

直接上代码:  代码如下 复制代码 <?php $my_array = array("a" => "Cat","b" => "Dog", "c" => "Horse"); extract($my_array); echo "$a = $a; $b = $b; $c = $c"; ?> 猜猜会会出现什么结果? 输出:  代码如下 复制代码

css3中 transition 与 animation用法介绍

在 CSS3 出现之前,网页上的动画都是靠 JavaScript 来实现的,在这个时代,你可能会经常看见这样的代码片段: setTimeout(funcntion() {   document.getElementById("test").style.opacity += 0.1; }, 300) 上面这段代码片段就实现了这个 id 所描述内容的透明度渐变. 这样写起来看上去比较复杂,可维护性也比较差.除此之外,在移动端的性能也很是捉急. CSS3 时代 石器时代终于过去,黎明已经到来

几种有用的变型 PHP中循环语句的用法介绍_php技巧

PHP手册中对它的语法定义如下: for (expr1; expr2; expr3) statement 下面说说for语句几种有用的变型. 1.无限循环 首先是人尽皆知的无限循环(亦可称"死循环").由于空表达式null在语法上是有效的,所以我们可以把for语句的三个表达式留空,这样就会产生不断执行for嵌套语句的效果. for (;;) { //放置需要不断执行的语句 } ?> 虽然有一些任务会使用到无限循环,但是大多数程序任务,特别是PHP所能涉及的领域,在使用无限循环时都

C++中strtok()函数的用法介绍_C 语言

函数原型:char *strtok(char *s, const char *delim);Function:分解字符串为一组字符串.s为要分解的字符串,delim为分隔符字符串.Description:strtok()用来将字符串分割成一个个片段.参数s指向欲分割的字符串,参数delim则为分割字符串,当strtok()在参数s的字符串中发现到参数delim的分割字符时 则会将该字符改为\0 字符.在第一次调用时,strtok()必需给予参数s字符串,往后的调用则将参数s设置成NULL.每次调

php中switch语句的用法介绍

Switch 语句 如果您希望有选择地执行若干代码块之一,请使用 Switch 语句. 使用 Switch 语句可以避免冗长的 if..elseif..else 代码块. 语法  代码如下 复制代码 switch (expression) { case label1:   code to be executed if expression = label1;   break;  case label2:   code to be executed if expression = label2;