🚩访问开辟的数组元素
int * p4 = new int [ 10 ] ;
cout << p4[ 0 ] << endl;
delete [ ] p4;
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
对于指针 p4,p4[i] 实际上等价于 *(p4 + i)
。也就是说,p4[0] 表示访问 p4 所指向的内存位置(即数组的第一个元素)的值
那么new和delete存在的意义是什么?
在C语言中
,malloc只完成了纯粹的开空间操作,虽然calloc也能对空间初始化,但是只能将所有元素初始化
在C++中
,new能够初始化部分元素
,比如在链表里能够调用构造函数
来完成初始化操作,省去了写BuyNewnode函数的麻烦
。delete相对于free会进行严格的类型检查
,确保释放的是new开辟的空间
,而且会调用析构函数
🔥值得注意的是:
申请和释放单个元素的空间
,使用new
和delete
操作符;申请和释放连续的空间
,使用new[]
和delete[]
,注意要匹配起来使用
2.2 operator new与operator delete函数
我们知道malloc是开空间
,new是通过开空间+构造函数实现的
,那么new的开空间可以直接调用malloc吗,答案是不可以的
首先我们要知道面向对象开空间失败喜欢抛异常而不是返回nullptr
🚩malloc开无限大空间
malloc开空间没有显示任何错误难以发现
🚩new开无限大空间
new开空间会在开空间失败后抛出异常
,用try...catch...捕获异常显示具体错误
void * __CRTDECL operator new ( size_t size) _THROW1 ( _STD bad_alloc)
{
void * p;
while ( ( p = malloc ( size) ) == 0 )
if ( _callnewh ( size) == 0 )
{
static const std:: bad_alloc nomem;
_RAISE ( nomem) ;
}
return ( p) ;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
所以根据operator new的底层代码
,new的开空间错误是要抛异常的,不能直接调用,而是new先调用operator new
,然后operator new再调用malloc开空间
。实际上malloc是被封装在了operator new里面的
对自定义类型进行操作时,调试状态下转到反汇编可以发现有两条call指令
,一条调用operator new
,一条调用构造函数
,也能证实我们上面的说法,operator delete也是同理
那么又延伸出另一个问题,operator new和operator delete的调用顺序是怎么样的?
假设类Stack
需要开辟动态数组_arry
,容量_capacity
,大小_size
Stack* p1 = new Stack;
delete p1;
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
p1作为指针变量存放地址
,在栈上存储
,然后new的空间在堆上开辟
,_array又指向开辟的数组
。如果按我们正常想的先调用operator delete释放堆空间
,那么_array指向的数组即使调用析构函数也找不到
,无法释放
因此我们可以整理出operator new和operator delete的调用顺序
调用operator new开辟空间 调用构造函数 调用析构函数 调用operator delete释放空间
2.3 定位new表达式
class A
{
public :
A ( int a = 0 )
: _a ( a)
{
cout << "A():" << this << endl;
}
~ A ( )
{
cout << "~A():" << this << endl;
}
private :
int _a;
} ;
int main ( )
{
A* p1 = ( A* ) malloc ( sizeof ( A) ) ;
new ( p1) A;
p1-> ~ A ( ) ;
free ( p1) ;
A* p2 = ( A* ) operator new ( sizeof ( A) ) ;
new ( p2) A ( 10 ) ;
p2-> ~ A ( ) ;
operator delete ( p2) ;
return 0 ;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象
,这里p1,p2只是个自定义类型,无法调用构造函数,所以要用定位new
其语法形式为:
new (place_address) type
或者new (place_address) type(initializer-list)
place_address
必须是一个指针
,initializer-list
是类型的初始化列表
在实际应用中,定位new一般用于池化技术
,也就是向内存申请一块内存池使用,因为频繁的向内存申请堆太麻烦了,所以申请一块内存池用于堆开辟空间
但是内存池的分配操作仅仅是对内存指针进行移动和管理,它只负责提供一块可用的原始内存
,并没有内存初始化的操作
当使用 new 操作符来创建对象
时,它会完成两个主要步骤:首先分配内存
,然后自动调用对象的构造函数对这块内存进行初始化
。但内存池的分配操作只是完成了第一步
,即提供内存,并没有触发构造函数调用的机制
,此时定位new的作用就体现出来了,显式调用构造函数实现初始化操作
3.关于内存管理的常见知识点
3.1 malloc/free和new/delete的区别
malloc/free和new/delete的共同点是:
都是从堆上申请空间
,并且需要用户手动释放
不同的地方是:
malloc和free是函数
,new和delete是操作符
malloc申请的空间不会初始化
,new可以初始化
malloc申请空间时,需要手动计算空间大小并传递
,new只需在其后跟上空间的类型即可
, 如果是多个对象,[]中指定对象个数即可
malloc的返回值为void*, 在使用时必须强转
,new不需要,因为new后跟的是空间的类型
malloc申请空间失败时,返回的是NULL
,因此使用时必须判空
,new不需要,但是new需要捕获异常
申请自定义类型对象时,malloc/free只会开辟空间
,不会调用构造函数与析构函数
,而new在申请空间后会调用构造函数完成对象的初始化
,delete在释放空间前会调用析构函数完成空间中资源的清理
3.2 内存泄漏
什么是内存泄漏?
内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况
。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制
,因而造成了内存的浪费
如何检测内存泄漏?
在vs下,可以使用windows操作系统提供的_CrtDumpMemoryLeaks()
函数进行简单检测,该函数只报出了大概泄漏了多少个字节,没有其他更准确的位置信息
int main ( )
{
int * p = new int [ 10 ] ;
_CrtDumpMemoryLeaks ( ) ;
return 0 ;
}
Detected memory leaks!
Dumping objects ->
{ 79 } normal block at 0x00EC5FB8 , 40 bytes long .
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
因此写代码时一定要小心,尤其是动态内存操作时,一定要记着释放。但有些情况下总是防不胜防,简单的可以采用上述方式快速定位下
。如果工程比较大,内存泄漏位置比较多,不太好查时一般都是借助第三方内存泄漏检测工具处理的
在linux下内存泄漏检测: linux下几款内存泄漏检测工具 在windows下使用第三方工具: VLD工具说明 其他工具: 内存泄漏工具比较
内存泄漏非常常见,解决方案分为两种
:
事前预防型
,如智能指针等事后查错型
,如泄漏检测工具
希望读者们多多三连支持
小编会继续更新
你们的鼓励就是我前进的动力!
id="blogVoteBox" style="width:400px;margin:auto;margin-top:12px" class="blog-vote-box">
id="blogExtensionBox" style="width:400px;margin:auto;margin-top:12px" class="blog-extension-box"> class="blog_extension blog_extension_type2" id="blog_extension">
class="extension_official" data-report-click="{"spm":"1001.2101.3001.6471"}">
class="blog_extension_card_cont">
欢迎交流~长期互三互推交流可加v
class="blog_extension_card_cont_r">
微信名片
评论记录:
回复评论: