C++动态内存管理

目录

1.了解进程空间

2.回忆C语言动态内存管理

3.认识C++动态内存管理

  (1)new 与 delete 操作内置类型

        <1>单个内置类型空间的申请与释放

        <2>连续多个内置类型空间的申请与释放

  (2)new 与 delete 操作自定义类型

        <1>单个自定义类型空间的申请与释放

        <2>连续多个自定义类型空间的申请与释放

4.new和delete使用的注意事项 

  (1)new与delete特点

  (2)malloc与free特点      

  (3)注意事项

5.new和delete的实现原理

  (1)相应汇编

  (2)new关键字的原理之 operator new() 函数

  (3)delete关键字的原理之 operator delete() 函数

6.总结


1.了解进程空间

        如下是程序运行时的进程空间:

        我们本文谈到的动态内存管理就是和堆相关的。C语言中的malloc、calloc、realloc所申请的空间就是在堆中。                

2.回忆C语言动态内存管理

        C语言中的动态内存管理中,我们经常使用malloc,calloc,realloc来在堆上申请空间,使用free释放在堆上申请的空间。下面来回忆一下malloc的使用:

        代码一:

//代码一
#include <stdio.h>
#include <malloc.h>

int main() {
	int* p = (int*)malloc(sizeof(int) * 10);
	free(p);
}

3.认识C++动态内存管理

  (1)new 与 delete 操作内置类型

        <1>单个内置类型空间的申请与释放

        new使用方法:1.new 类型。(不初始化)   2. new 类型(初始值)

        delete使用方法:delepte 变量名

        代码二:申请单个内置类型的空间。(本文代码均在win10系统下的vs2019下运行)

//代码二
#include "iostream"
using namespace std;

int main() {
	//单个类型空间的申请
	int* p1 = new int;
	//单个类型空间的申请并初始化
	int* p2 = new int(10);

	//单个类型空间的释放
	delete p1;
	delete p2;
}

        <2>连续多个内置类型空间的申请与释放

        首先来了解一个知识点,int arr[10]的类型是什么?类型是:int [10]。之所以形式与其他内置类型不一样是因为这是语法规定。

        new []使用方法:1.new 类型。(此处的类型是指上述中的数组类型,并且这种形式没有赋初始值)  2.new 类型{初始值}(此处类型也是上述中的数组类型)

        delete[]使用方法:delete[] 变量名

        代码三:

//代码三
#include "iostream"
using namespace std;

int main() {
	//一段连续内置类型空间的申请
	int* p3 = new int[10];
	//申请一段连续的内置类型空间并初始化
	int* p4 = new int[10]{ 1,2,3,4 };

	//释放一段连续的内置类型空间
	delete[] p3;
	delete[] p4;
}

  (2)new 与 delete 操作自定义类型

        注意:new会调用构造函数,delete会调用析构函数。(现在知道这一点对于理解它的用法很有帮助)

        <1>单个自定义类型空间的申请与释放

          [1].只有默认构造函数的自定义类型。(注意:如果没有显式定义构造函数,就不可以在new的括号后面赋初值,因为new需要调用构造函数,没有实现,怎么可以赋初值?)

        代码四:

//代码四
#include "iostream"
using namespace std;

class A {
public:
	int _m = 0;
};

int main() {
	A* pa = new A;
	delete pa;
}

          [2].显式定义有参构造函数后,就不可以使用上述方式赋值。(因为,new会调用构造函数,除非你显示实现了无参的构造函数,否则那样必然是会报错的。)      

        代码五:

//代码五
#include "iostream"
using namespace std;

class A {
public:
	int _m = 0;
	A(int m)
		:_m(m)
	{}
};

int main() {
	A* pa = new A(5);
	delete pa;
}

        <2>连续多个自定义类型空间的申请与释放

          [1].只有默认构造函数的自定义类型。(注意事项和单个自定义类型相同)

        代码六:

//代码六
#include "iostream"
using namespace std;

class A {
public:
	int _m = 0;
};

int main() {
	A* pa = new A[10];
	delete pa;
}

          [2].显式定义构造有参函数后。(注意事项和单个自定义类型相同)

        代码七:

        注意:[]内的数字代表数组中对象个数,{}中的是为每个对象赋的初值。在实际使用中我发现,初值的个数与对象个数要完全相同,否则报错。这一点和内置类型中的不同。

//代码七
#include "iostream"
using namespace std;

class A {
public:
	int _m = 0;
	A(int m)
		:_m(m)
	{}
};

int main() {
	A* pa = new A[2]{3,3};
	delete pa;
}

4.new和delete使用的注意事项 

  (1)new与delete特点

        <1>new会调用构造函数,delete会调用析构函数。

        代码八:

//代码八
#include "iostream"
using namespace std;

class A {
private:
	int _m;
	int _n;
public:
	A(int m, int n)
		:_m(m)
		, _n(n)
	{
		cout << "带参构造调用" << endl;
	}

	A() {
		cout << "无参构造调用" << endl;
	}

	~A() {
		cout << "析构调用" << endl;
	}
};

int main() {
	A* pa1 = new A;
	delete pa1;
	cout << "=============" << endl;
	A* pa2 = new A(12, 13);
	delete pa2;
}

        上述代码的打印结果:从中可以验证,上述结论。

          <2> new 和 delete应该配套使用,否则可能会有内存泄漏或者程序崩溃。

        此处的配套使用是指:用new申请的空间要用delete释放。用malloc、calloc、realloc申请的空间要用free释放。

  (2)malloc与free特点      

         malloc不会调用构造函数,free不会调用析构函数。

        代码九:

//代码九
#include "iostream"
using namespace std;

class A {
private:
	int _m;
	int _n;
public:
	A(int m, int n)
		:_m(m)
		, _n(n)
	{
		cout << "带参构造调用" << endl;
	}

	A() {
		cout << "无参构造调用" << endl;
	}

	~A() {
		cout << "析构调用" << endl;
	}
};

int main() {
	A* pa1 = (A*)malloc(sizeof(A));
	free(pa1);
	cout << "=============" << endl;
	A* pa2 = (A*)malloc(sizeof(A));
	free(pa2);
}

        上述代码的结果打印:

  (3)注意事项

        <1>操作内置类型时,如果不配套使用,一般不会出现内存泄漏。我们用内置的_CrtDumpMemoryLeaks() 函数来检测是否存在内存泄漏。

        代码十:如下代码可以在输出窗口中看到检测结果,并没有内存泄漏。

//代码十
#include "iostream"
using namespace std;

int main() {
	int* p1 = new int(10);
	free(p1);

	_CrtDumpMemoryLeaks();
}

        <2>操作自定义类型,如果不配套使用,会内存泄漏 或 程序崩溃。

          [1]验证程序崩溃

        代码十一:这段代码会报错,并且是在析构函数内报错,原因如下。

        首先,使用malloc会申请一段空间,但因为没有初始化,所以空间中全是随机值,作为类中成员变量的指针p中自然也就是保存的随机值。

        可是释放资源是使用的是delete,这个已经验证过了,会调用析构函数。可是p中保存的是随机值,也就是说p是一个野指针,释放一个野指针怎么会不报错呢?

//代码十一
#include "iostream"
using namespace std;

class A {
	int* p;
public:
	A() {
		p = (int*)malloc(100);
		cout << "构造调用" << endl;
	}
	~A() {
		free(p);
		p = nullptr;
		cout << "析构调用" << endl;
	}
};

int main() {
	A* pa = (A*)malloc(sizeof(A));
	delete pa;

	_CrtDumpMemoryLeaks();
}

          [2]验证内存泄漏       

        代码十二:

//代码十二
#include "iostream"
using namespace std;

class A {
	int* p;
public:
	A() {
		p = (int*)malloc(100);
		cout << "构造调用" << endl;
	}
	~A() {
		free(p);
		p = nullptr;
		cout << "析构调用" << endl;
	}
};

int main() {
	A* pa = new A;
	free(pa);

	_CrtDumpMemoryLeaks();
}

        如下是这段代码的输出窗口图和打印窗口图:

        如下图是打印窗口图:可以看到,只调用了构造函数,没有调用析构函数。

        如图是经过处理后的输出窗口图:可以看检测结果有100字节的内存泄漏,就是因为构造函数中申请的空间没有释放。

5.new和delete的实现原理

  (1)相应汇编

        首先看一下如下代码的汇编:

        代码十三:

//代码十三
#include "iostream"
using namespace std;

class A {
public:
	int _m = 0;
	A(int m)
		:_m(m)
	{}
};

int main() {
	A* pa = new A[2]{3,3};
	delete pa;
}

        以下是经过处理后的汇编语句,看一下重点即可:

        

         可以看到,new关键字申请空间时,底层会先调用一个operator new()函数,然后再调用构造函数初始化。delete关键字释放空间时,底层会调用一个operator delete()函数。

        注意:new和delete是关键字,operator new 和 operator delete是函数。

  (2)new关键字的原理之 operator new() 函数

        new T:T是自定义类型

        1.申请sizeof(T)大小的空间。

          [1]此时operator new()函数通过调用 malloc 循环申请空间。若成功,直接返回空间的首地址。若失败,执行[2]。

          [2]检测用户是否提供应对空间不足的函数。若提供,继续执行[1]。若未提供,抛出异常。

        2.调用T的构造函数对空间进行初始化形成对象。

        这一步就不需要解释了,直接调用构造函数,在申请好的空间上进行初始化。

  (3)delete关键字的原理之 operator delete() 函数

        1.先调用析构函数,将对象中的资源清理干净

        2.调用operator delete释放对象的空间。

        operator delete内部调用free来释放对象的空间

6.总结

        <1>malloc(size):只是从堆上申请size个字节的空间,并不会对空间中的内容进行初始化。即:不会调用构造函数。

        <2>free(p):只负责将p指向的堆空间还给系统,并不会调用析构函数对空间中的内容进行清理。即:不会调用析构函数。

        <3>new:会申请空间,其次会调用构造函数对空间中的内容进行初始化。

        <4>delete:会释放空间,并且会调用析构函数对空间中的资源进行清理。

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值