new和delete与内存分配

一、Effective C++条款16:成对使用new和delete时采用相同的形式

通常我们使用new和delete有两种情形,第一,动态的为单一对象分配内存,第二,动态的创建数组。new和delete的使用需遵循许多规则,这里着重理解相同形式这一关键词。且看下面的动作有什么错?

std::string *stringArray = new std::string[100];

...
delete stringArray;
乍看起来,同时使用了new和delete,似乎没什么错误。这里使用new在运行阶段动态分配了100string对象,但是仅使用delete只释放掉了单一对象的所占用的内存,其中99个string对象不太可能被适当的删除,因为它们的析构函数没有被调用。

请注意:这里仅仅同时使用了new和delete,但并没有满足相同的形式这一条件。我们所面临的问题可以更简单的叙述为:即将被删除的那个指针,所指的是单一对象还是对象数组?另外,请尽量使用C++11智能指针,以防止写代码时忘记释放掉new分配的内存而导致内存泄漏。

请记住

如果在new表达式中使用了[],必须在相应的delete表达式中使用[]。如果在new表达式中不使用[],一定不要在相应的delete表达式中使用[]。确保“同时使用、相同形式”。

二、使用new动态分配内存

为单一对象(可以是数据结构,基本类型或类)获得并指定分配内存的通用格式如下:

typeName *pointter_name  = new typeName;

例如,在运行阶段为一个int值分配未命名的内存,并使用指针来访问这个值,可以写为:int * pn = new int;

程序员告诉new要为int类型的数据类型分配内存,new根据这种数据类型找到长度(在这里int为4个字节)正确的内存块,并返回内存块的地址,程序员负责将该地址赋给一个指针(pn是被声明为指向int的指针,现在pn是一个地址,而*pn为存储在那里的值)。


请看下面的例子:

int *pt = new int;

*pt = 1001;

double *pd = new double;

*pd = 3.14;


则有:如果这里int占4个字节,double占8个字节,则sizeof(pt)为4,sizeof(*pt)为4。sizeof(pd)为4,sizeof(*pd)为8。这里,指向int的指针的长度与指向double的指针相同,它们都是地址,但由于声明了指针的类型,因此程序知道*pd为8个字节的double值,*pt为4个字节的int值。

三、使用new和delete时要遵循的规则

  • 不要使用delete释放不是new分配的内存。
  • 不要使用delete释放同一个内存两次。
  • 使用new[]为数组分配的内存一定要使用delete[]释放,不能使用delete;new为单个对象分配的内存一定使用delete来释放。
  • 对空指针delete是安全的。

四、使用动态数组

创建动态数组后,如何使用数组中的元素?

int *pt = new int[10]; // get  a block of 10 ints

如果这里int占4个字节,上述语句中sizeof(pt)为4个字节,sizeof(*pt)为4 × 10个字节。那么如何使用数组中的元素呢?其实和普通数组一样。


double *pd = new double[3];

pd[0] = 1.0;

pd[1] = 2.0;

pd[2] = 3.0;


那么,pd[0](或*pd) 的值为1.0。如果有pd = pd + 1;, 那么pd[0]的值为2.0。另外请注意,不能修改数组名(常量),但指针(pd)为变量,因此可以修改它的值。而且pd+1的效果是指指针指向数组第一个元素的下一个位置,即第二个元素,因此pd[0]为2.0。

五、杂项

指针和数组等价的原因在于指针算术和C++内部处理数组的方式。首先我们来看一下算术,将整型变量加1后,其值将增加1;但将指针变量增加1后,增加的量等于它指向的数据类型的字节数,如:将指向double的指针加1后,如果系统对double使用8个字节存储,则其值增加8。另外,C++将数组名解释为数组第一个元素的地址。

double arrD[3] = {1.0, 2.0, 3.0};

short arrSh[3] = {1, 2, 3};

double *pd = arrD; // 等价于&arrD[0]

short *psh = &arrSh[0];

...

假设double占4个字节,short占2个字节,则:arrD[0] ,*arrD和*(arrD + 0)等价,其值为1;*psh为1。

sizeof(arrD)为3 × 8 = 24,sizeof(pd)为4,sizeof(*pd)为8。

sizeof(arrSh)为3 × 2 = 6,sizeof(psh)为4,sizeof(*psh)为2。

这是因为 指针为指向变量的地址,且地址占4个字节;对指针的解引用为指针指向变量的值,所占内存谓该值所属类型所占的字节数。



注意:虽然数组名被解释为指向数组第一个元素的地址,但不能修改数组名,是因为数组名为常量;可以修改指针,是因为指针(指向变量的地址)为变量。

pointerName = pointerName + 1; // valid

arrayName = arrayName + 1; // not alllowed


数组的地址

数组名被解释为其第一个元素的地址,而对数组名应用地址运算符时,得到的是整个数组的地址。

short tell[10];     // tell为20字节的数组

cout << tell ;    // &tell[0] 即数组第一个元素的地址,占2个字节的内存块的地址

cout << &tell;  //  整个数组的地址,从数字上说,该地址和tell的地址相同,但是&tell为占20个字节的内存块的地址


总之,使用new来创建数组以及使用指针来访问不同的元素时,只需要把指针当成数组名对待即可。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值