使用new和delete(C语言中使用函数malloc( ))分配的内存,我们叫做动态内存。动态内存由new和delete控制,不像前述的变量内存分配是由作用域和链接性规则决定,而动态内存分配取决于new和delete何时被使用。通常,编译器分配3种内存:一块用于静态变量(可能再细分),一块用于自动变量,一块用于动态存储。
new运算符
1. 使用new运算符时的初始化
如何初始化动态分配的变量:
- c++98中:为内置类型(double,int等)分配存储空间并初始化,可以在类型名后面加上用括号括起来的初始值。例如:
int* pt=new int (6);
double* pc=new double (0.32);
- c++11中:既支持c++98方式的初始化,新增对结构或数组的初始化,常使用大括号的列表初始化 。还支持将列表初始化用于单值变量。例如:
struct student
{
char name[20];
int class;
}
student* one=new student {"Mary",2}; //new初始化结构
int* pm=new int[4]{2,4,6,8}; //new初始化数组
int* ps=new int {4};
2. new失败时
new可能找不到请求的内存量。在最初的10年间,c++在这种情况下让new返回空指针,但现在将引发异常 std:bad_alloc。
3. new:运算符,函数和替换函数
new运算符分配动态内存时,运算符 new 和 new [ ] 分别调用如下函数:
void* operator new (std::size_t); //使用new
void* operator new [] (std::size_t); //使用new[]
这些函数叫做 分配函数 。位于名称空间内,同样地,在使用 delete 和 delete[] 运算符时,也会调用类似函数。此处 size_t 是一个typedef,对应于合适的整形。
例如,对于定义一个int分配内存时:基本语句
int* ps=new int {4};
调用函数,将会被转换为如下:
int* ps=new int(sizeof(int));
而数组分配内存时:
int* ph=new int [5];
调用new[] ,会被转换为:
int* ps=new int(5*sizeof(int));
4. 定位运算符new
new运算符在以上基础之上,还有另一个变体。被称为new定位运算符,它能够让使用者指定要使用的内存地址。因此可以使用new来设置其内存管理规程,处理需要通过特定地址进行访问的硬件或在特定位置创建对象。
定位new运算符使用:
包含头文件 new ,它提供了 new 运算符的原型;
然后将 new 运算符用于已经提供了的地址的参数;
其它句法与常规new 相同。
示例如下:
#include <iostream>
#include <new>
using namespace std;
....
char buffer[50];
int main()
{
int* p1=new int (4);//常规new
int* p2=new int [4];//常规new数组
int* p3=new (buffer) int;//定位new,从buffer数组开始地址处分配1个int内存
int* p4=new (buffer +sizeof(int)) int [4];//定位new,从buffer数组偏移sizeof(int)量的地址处开始分配4个int内存
.....
return 0;
}
如下程序演示了定位new 和常规 new的使用:分别使用常规new和定位new创建
#include <iostream>
#include <new>
using namespace std;
const int N=4;
const int BUFF=512;
char buffer[BUFF];
int main()
{
/************************************************************************/
double* p1=new double [N];
double* p2=new (buffer) double [N];
for (int i=0;i<N;i++)
{
p1[i]=p2[i]=1000+30*i;
}
cout<<"常规 new 和 定位 new :\n";
cout<<"内存地址 :\n";
cout<<"堆开始地址 :"<<p1<<" 静态数组buffer内存区开始地址 :"<<p2<<endl;
cout<<"内存区内容 :\n";
for (int i=0;i<N;i++)
{
cout<<p1[i]<<" at "<<&p1[i]<<" ; "<<p2[i]<<" at "<<&p2[i]<<"\n";
}
/************************************************************************/
double* p3=new double [N];
double* p4=new (buffer) double [N];
for (int i=0;i<N;i++)
{
p3[i]=p4[i]=1000+60*i;
}
cout<<"常规 new 和 定位 new :\n";
cout<<"内存区内容 :\n";
for (int i=0;i<N;i++)
{
cout<<p3[i]<<" at "<<&p3[i]<<" ; "<<p4[i]<<" at "<<&p4[i]<<"\n";
}
/************************************************************************/
delete [] p1;
p1=new double [N];
p2=new (buffer +N*sizeof(double)) double [N];
for (int i=0;i<N;i++)
{
p1[i]=p2[i]=1000+80*i;
}
cout<<"常规 new 和 定位 new :\n";
cout<<"内存区内容 :\n";
for (int i=0;i<N;i++)
{
cout<<p1[i]<<" at "<<&p1[i]<<" ; "<<p2[i]<<" at "<<&p2[i]<<"\n";
}
delete [] p1;
delete [] p3;
return 0;
}
输出结果:
程序分析:
- 定位 new 运算符将 p2 (p4)放在了数组 buffer 中,p2( p4 )和buffer数组地址相同;
- 常规 new 运算符p3将查找一块新的内存块,起始地址与 p1 的不同;
- 定位 new 运算符 p2 被重新分配,在buffer数组加上偏移量N*sizeof(double)的地址处;
- 常规 new 运算符必须使用 delete[ ] 来释放,而定位 new 不使用 delete 释放,否则会出错误。
5. 定位运算符 new 函数
与常规 new 运算符一样,定位 new 也会调用函数 ,标准 定位 new 调用一个接收 2个参数的 new ( ) 函数: