动态对象创建-----new和delete

 C++中的newdelete是如何在堆上安全地创建对象

为对象分配内存的三种方式:
1) 静态内存分配
在程序开始之前就已分配好了存储空间,直到程序结束
2) 在栈上分配内存
当程序运行到一个执行点(譬如以“ { ”开始的一个程序块),存储空间在栈上被创建,当出这个这程序块时,自动释放。如果为一些局部变量分配存储单元一样。这种分配方法分配的存储空间大小是固定的,且要在程序中明确指定所要分配的存储空间的大小。 ( 对象的大小、生存期被内置在编译器所生成的代码里 )
3) 在堆上动态分配内存
是由程序在执行的过程中根据需要在堆上动态分配的,它的大小可根据需要变化。需要程序员手动释放这些空间。生存期不受所在存储块范围的影响。(要比在栈上分配内存更多开销)
 
       ( C++ 不允许直接将 void* 型指针赋给任何其它类型的指针,要想赋值必须先进行强制类型转换。
 
C++ 中类的构造函数不能被显式调用,而在创建对象由编译器调用的。
 
operator new
         所做的事情:
1)  计算并分配所需的存储空间;
2)  检查是否成功地分配了存储空间;
3)  将所分配的存储空间进行类型转换(转换为所分配给的那个类类型),返回指向所分配存储空间的指针给类的构造函数。
它使得在堆里和在栈里创建一个对象一样容易。
 
用于普通单个对象的 new:
delete 一个 void* 指针,将不会调用任何析构函数。因为它不确定是什么类型的指针也不确定调用哪个析构函数。执行这个操作可能引起一个错误。
如果一个类里面有 void* 类型的指针,那么在 delete 时,不会释放 void* 的指针所指向的内存,这样就会引起内存泄露。为避免这种现象,可以在 delete 之前所它转换成非 void* 的合适的类型的指针。
 
如果类的构造函数没有参数,那么可以写没有构造函数参数表的 new 表达式(如: A *a=new A; )。
我想这也就解释了:为什么用 new 动态创建一个对象时,总是先定义一个类对象指针,然后用 new 表达式并赋值给这个指针?为什么有时候 new 表达式后只是一个构造函数名,有时是一个有参数表的构造函数名,有时又跟是一个空参数表的构造函数名 ?
 class A{
 A();
};
A *a=new A; A *a=newA(); 是一样的了。
 
用于数组的 new :
要求:必须有一个默认的构造函数,以方便对数组里每一个对象都调用不带参数的构造函数。
A *a=new A[100];// 那么 new 表达式将返回一个数组指针赋值给 a ,这个数组是一个由指向 A 类对象的指针为元素的数组,也就是说 a 是一个 ( 包含 100 ( 指向 A 类对象的指针 ) 元素的 ) 数组指针。所以在释放 a 所指向的内存时,就不能像 delete 普通对象那样,而是要 delete 一个对象指针数组指针。具体看 operator delete 说明。
由于一个指针变量可以改为指向任意其它类型,而对于如上所说的数组指针 a 的修改没有意义,且会引起一些错误,所以为避免这种情况的发生,我们可以将指针 a 定义为一个指针常量 (A* const a=new A[100];) ,这样也不会数组里的对象指针有影响。(注意:不能写成了 A const *p=new A[100]; const A *p=new A[100]; ,这样就成了让数组里的元素 ( 对象指针 ) 为常量,而数组指针仍然是变量。)
 
在用 new 为一个对象分配内存时,如果没有连续的足够大的内存分配给这个对象,那就将会调用 new-handle() 函数,抛出一个异常。
 
 
operator delete
         delete 表达式所做的事情:
1)  取回创建数组时所记下的对象数; ( 如果是一个对象数组 , 如: delete [ ]a; ,由 [ ] 告诉编译器需要做这件事 )
2)  调用每个对象的析构函数
3)  释放为每个对象所分配的存储空间(一般是用 free()
* 如上第1)步只在对象数组时才需要,如果是普通的单个非数组对象指针则不需要这一步
delete 需要一个对象的地址(指针)。它只放释放 new 所分配的存储空间,如果用 delete 释放一个 malloc() calloc() 的存储空间,动作是未定义的。
delete 释放空间之后,并不会自动把指向它的指针赋值为空,为避免对同一空间多次释放,一般在调用释放之后(在这里就是 delete 之后)手动将所指向它的指针赋值为0,而对一个空指针多次释放是不会有问题的(因为释放一个空指针什么也不发生)。
delete a;
a=0;
 
重载 new delete:
这样做只是改变 new/delete 表达式里内存分配和释放部分,不也无法改变调用构造函数和析构函数的部分。
全局的 new/delete 重载:
对全局 new()/delete() 的重载将使默认的 new()/delete() 完全不能被使用,甚至连这个重载版本里也不能被使用。
new() 的参数:必须有一个 size_t 类型的参数,它由编译器传递给我们,是所要分配内存空间的对象的大小,在使用 new 时通常看不这个参数的显式传递;
new() 的返回值:一个 void* 类型的指针(如果分配内存不成功,将返回 0 )。
在函数里面应该对内存分配不成功做处理(如调用 new-handle() 或抛出异常或重新分配内存等等之类)。
delete() 的参数:一个 void* 指针(这是因为先调用了析构函数后得到的指针),
delete() 的返回值:返回类型为 void
一个类的 new/delete 重载:
在一个类里重载的 new/delete 只是对当前这个类默认不使用全局的 new/delete ,在其它地方其它类里面仍然可使用全局的 new/delete 。但是,对于已重载 new/delete 的类,如果创建它的对象数组,仍然使用的是全局的 new/delete ,除非也对它重载了创建对象数组版本的 new[]/delete[ ]
         重载 new delete 的其它用处:
1) 把对象放在指定的内存位置上,这种方法可以在 new() 的参数中指定对象所放的位置,也可以加其它更多的参数;
2) 采用特定的内存分配方案方案。
显式调用析构函数会引起一些问题: 1 )对于栈上创建的对象,会导致两次调用对象的析构函数(因为在栈对象作用域结束的位置会自动 destroy 对象); 2 )对于堆上创建的对象,显式调用析构函数后并没有释放内存。所以在这两种情况下都不显式调用析构函数,要显式调用析构函数,只有一种情况:支持 operator new() 的定位语法时,也就是用于把对象放在指定的内存位置的情况。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值