C++内存管理 && 读高质量C++/C编程指南第7章
Posted TangguTae
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++内存管理 && 读高质量C++/C编程指南第7章相关的知识,希望对你有一定的参考价值。
目录
operator new与operator delete函数
operator new[ ]与operator delete[ ]函数
这次来重点关注一下C++中的内存管理
内存区域划分
首先来看一下C/C++中程序的内存区域划分
从下往上依次为:代码段(常量区)、数据段(静态区)、堆、栈、内核空间。以32位系统为例,总共的地址空间为2^32,即为4G的大小,其中内核空间占用1G的大小,用户空间占用3G的大小。
内存的分配方式
1、代码段也叫常量区,用来存放二进制代码和常量;
2、数据段也叫静态区,用来存放全局变量、static变量。该区域的内存在程序编译的时候就已经分配好了,这块内存在程序于整个运行期间都存在。
3、从堆上分配的空间叫做动态内存分配,程序在运行的时候调用malloc或者new申请任意多少的内存,程序员自己负责free或者delete释放内存(如果不释放会造成内存泄漏)。
4、函数内部的局部变量是在栈上开辟的空间,函数在执行结束时这些变量会自动释放。
new / delete
在C语言中,我们用malloc 和 free来进行内存的开辟与释放,在C++中我们既可以用malloc 和 free,也可以用new 和 delete。所以为什么要引入new 和 delete?
比较new / delete与malloc / free
在内置的类型的效果是一样的
对于自定义类型效果不一样的
光用malloc 和 free 是无法满足动态对象的要求的。malloc不会调用构造函数初始化;free只释放空间,不会调用析构函数。但是new开辟空间会调用构造函数初始化;delete释放空间并调用析构函数。
由于C++是一种面向对象的语言,用malloc和free无法完成动态对象的内存管理,应该用new和delete。
new与delete的实现原理
operator new与operator delete函数
new和delete是用户进行动态内存申请和释放的操作符;operator new 和operator delete是系统提供的全局函数;new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。
例如:
int *p=(int*)operator new(sizeof(int));
int *p = (int*)malloc(sizeof(int));
二者形式上很相似,实际上还是有区别的
malloc失败会返回一个空指针,但是operator new开辟失败会抛异常。
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则就抛异常。
operator delete 最终调用free函数,但是如果释放失败和free一样直接终止程序。
operator new[ ]与operator delete[ ]函数
如果我们要new出多个对象时即new T[N],底层其实调用operator new[]来实现.。同样的delete[]调用的是operator delete[]。
void* operator new[](size_t _Size);
void*operator delete[](void* _Block);
operator new[]开辟多个空间时,调用的是operator new完成N个对象的空间申请
operator delete[]释放多个空间时,调用operator delete来释放空间
总结
new的原理分两步:1、调用operator new来申请空间;2、调用对象的构造函数。
delete的原理也分两步:1、调用析构函数对对象进行析构;2、调用operator delete释放空间
new[]的原理:1、调用operator new[]开辟N个对象大小的空间申请;2、调用N次对象的构造函数
delete[]的原理:1、调用N次对象的析构函数;2、调用operator delete[]对N个对象空间进行释放
原理讲完了,现在来看看实际编程中应该注意的地方
常见的内存错误
1、内存尚未分配成功,却使用了它
建议每次申请完内存空间时,先检查指针是否为空。
2、相关操作导致内存越界
这个只能编程者自己小心了,尤其是在循环语句中很容易发生越界(例如数组)。
3、内存泄漏
动态内存的申请与释放必须匹配,无论是malloc/free还是new/delete,一个malloc对应一个free,new和delete同理。
这里还有个细节,当调用new[]一次开辟多个对象空间,在释放资源的时候只调用了delete,而未调用delete[],这样也会导致内存泄漏。前者相当于delete objects[0],只释放了一个对象的资源。
除此之外,在网络通信的过程中,socket套接字的创建但是忘记释放也会造成系统资源的泄漏。
为了避免内存泄漏,建议:
- 程序员自身养成良好的编程习惯。
- 采用C++11中的智能指针来管理指针。
- 打开的文件描述符记得释放。
4、释放内存还继续使用该内存空间
(1)程序对象调用关系过于复杂,不清楚某个对象是否已经被释放。
(2)函数返回了指向栈内存的指针或者引用,该内存出函数体后自动被销毁。
(3)使用free或者delete释放了指针,但是未将指针设为空(野指针)。
指针与数组的对比
数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。数组名对应着(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变。
指针可以随时指向任意类型的内存块,它的特征是“可变”,所以我们常用指针来操作动态内存。
避免野指针的出现
野指针的出现主要有以下几种情况
1、指针变量没有初始化
任何指针变量刚被创建时不会自动成为NULL 指针,它的缺省值是随机的。所以需要进行初始化或者指向合法的内存空间。
char * p = NULL;//初始化
char * str = (char*)malloc(100*sizeof(char));//指向合法的内存
2、指针被释放后,未将该指针置空
当指针被free 或者delete 之后,没有置为NULL。
3、指针操作超越了变量的作用范围
class A
public:
void Func(void)
;
void test()
A *p;
A a;
p = &a;
//此时代码就有风险,因为a除了作用域
p->Func();
当a出了作用域以后,p所指向的空间已经被释放,此时p就成为了野指针,这种代码非常的危险,而且还不容易察觉的到,编译器也没有报错。
以上是关于C++内存管理 && 读高质量C++/C编程指南第7章的主要内容,如果未能解决你的问题,请参考以下文章
C++初阶:内存管理C/C++内存分布及管理方式 | new/delete实现原理及operator new和operator delete函数