C++ new的用法
知识点小结
new其实就是告诉计算机开辟一段新的空间,但是和一般的声明不同的是,new开辟的空间在堆上,而一般声明的变量存放在栈上。
通常来说,当在局部函数中new出一段新的空间,该段空间在局部函数调用结束后仍然能够使用,可以用来向主函数传递参数。
另外需要注意的是,new的使用格式,new出来的是一段空间的首地址。所以一般需要用指针来存放这段地址。
具体的代码如下:
#include <iostream>
using namespace std;
int example1()
{
//可以在new后面直接赋值
int *p = new int(3);
//也可以单独赋值
//*p = 3;
//如果不想使用指针,可以定义一个变量,在new之前用“*”表示new出来的内容
int q = *new int;
q = 1;
cout << q << endl;
return *p;
}
int* example2()
{
//当new一个数组时,同样用一个指针接住数组的首地址
int *q = new int[3];
for(int i=0; i<3; i++)
q[i] = i;
return q;
}
struct student
{
string name;
int score;
};
student* example3()
{
//这里是用一个结构体指针接住结构体数组的首地址
//对于结构体指针,个人认为目前这种赋值方法比较方便
student *stlist = new student[3]{{"abc", 90}, {"bac", 78}, {"ccd", 93}};
return stlist;
}
int main()
{
int e1 = example1();
cout <<"e1: "<< e1 << endl;
int *e2 = example2();
for(int i=0; i<3; i++)
cout << e2[i] << " ";
cout << endl;
student *st1 = example3();
for(int i=0; i<3; i++)
cout << st1[i].name << " " << st1[i].score << endl;
return 0;
}
new的三种使用方法
概念
在C++中new的三种用法包括:plain new, nothrow new 和 placement new。
plain new 就是我们最常使用的new的方式,在C++中的定义如下:
1 void* operator new(std::size_t) throw(std::bad_alloc);
2 void operator delete( void *) throw();
plain new在分配失败的情况下,抛出异常std::bad_alloc而不是返回NULL,因此通过判断返回值是否为NULL是徒劳的。
nothrow new 是不抛出异常的运算符new的形式。nothrow new在失败时,返回NULL。定义如下:
1 void * operator new(std::size_t, const std::nothrow_t&) throw();
2 void operator delete(void*) throw();
placement new 意即“放置”,这种new允许在一块已经分配成功的内存上重新构造对象或对象数组。placement new不用担心内存分配失败,因为它根本不分配内存,它做的唯一一件事情就是调用对象的构造函数。定义如下:
1 void* operator new(size_t, void*);
2 void operator delete(void*, void*);
palcement new 的主要用途就是反复使用一块较大的动态分配的内存来构造不同类型的对象或者他们的数组。placement new构造起来的对象或其数组,要显示的调用他们的析构函数来销毁,千万不要使用delete。
示例
char *getMemory(unsigned long size)
{
char * p = new char[size];
return p;
}
void main(void)
{
try{
char * p = getMemory(1000000); // 可能发生异常
// ...
delete [] p;
}
catch(const std::bad_alloc & ex)
{
cout << ex.what();
}
}
void func(unsinged long length)
{
unsinged char * p = new(nothrow) unsinged char[length];
// 在使用这种new时要加(nothrow) ,明示不使用异常处理 。
if (p == NULL) // 因不抛异常,故定要检查
cout << "allocte failed !";
// ...
delete [] p;
}
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版竟然能运行而不出错!
该运算符的作用是:只要第一次分配成功,不再担心分配失败。
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 的对象的大小不再与原空间相同。
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的事,即要使用原来的指针释放内存。