CD30.【C++ Dev】类和对象(21) 内存管理(上)

目录

1.前置知识

易错点

汇编语言底层分析常量区的字符串拷贝到栈区

2.new和delete

知识回顾

分析new和delete的细节

new

不带参的构造函数

带参的构造函数

delete

new和malloc不能混用,delete和free也不能混用

使用new和delete操作对象时,构造函数和析构顺序


1.前置知识

明确以下内容:

程序运行会产生一些数据,而这些数据可以是局部数据、静态数据、全局数据、常量数据、动态申请的数据等,数据需要在内存中的不同区域存储,这些不同区域为:栈区、堆区、静态区(又称数据段,存储全局数据和静态数据)、常量区(又称代码段,存储可执行代码和只读常量)等

 可以看看73.【C语言】C/C++的内存区域划分文章进一步了解内存区域的划分

易错点

以下代码中ptr和*ptr各存储在什么区域?

#include <iostream>
using namespace std;
int main()
{
	const char* ptr1 = "teststring";
	const char ptr2[] = "teststring";
	return 0;
}

ptr1和ptr2都是指针,因此都存储在栈区(在main函数的栈帧空间中),*ptr是字符串"teststring"存储在常量区(ptr1使用const修饰,常量字符串具有常性,仅只读),因为ptr1指向的是常量字符串

但ptr2指向的是字符串数组{"teststring"},该字符串数组位于栈区,"teststring"存储在常量区,但会拷贝一份给栈区,因此ptr2指向的是栈区中的"teststring"

汇编语言底层分析常量区的字符串拷贝到栈区

环境选Debug+x86,打开调试模式后转到反汇编代码,只截取有用的代码:

	const char* ptr1 = "teststring";
 mov         dword ptr [ptr1],offset string "teststring" (04F9C00h)  
	const char ptr2[] = "teststring";
 mov         eax,dword ptr [string "teststring" (04F9C00h)]  
 mov         dword ptr [ptr2],eax  
 mov         ecx,dword ptr ds:[4F9C04h]  
 mov         dword ptr [ebp-18h],ecx  
 mov         dx,word ptr ds:[4F9C08h]  
 mov         word ptr [ebp-14h],dx  
 movzx       eax,byte ptr ds:[4F9C0Ah]  
 mov         byte ptr [ebp-12h],al  

注:movzx(全称move with zero-extend)的作用是将源操作数的值移动到目标操作数中,并且将目标操作数的高位部分用0填充

整个字符串的拷贝拆成4部分来分块拷贝

第一块:"test",由eax做中转寄存器

第二块:"stri",由ecx做中转寄存器

第三块:"ng",由dx做中转寄存器

第四块:隐藏的"\0",由eax做中转寄存器,只取al(eax的低8位)

详细见下图:

2.new和delete

知识回顾

之前在CC26.【C++ Cont】动态内存管理(new和delete)浅析和面向对象的方式实现链表文章中讲过new和delete的基本使用方法本文不再赘述,但讲得比较浅,本文讲深入分析new和delete未提到的点

分析new和delete的细节

new

操作符new的作用是:开辟空间并调用构造函数初始化,这是malloc所不具备

如果对内置类型调用new,这与调用malloc开起来没有区别,但如果是自定义类型就有区别,例如以下代码:

不带参的构造函数
#include <iostream>
using namespace std;
class Myclass
{
public:
	Myclass()
		:_val(1)
	{
		cout << "Myclass()" << endl;
	}
	int _val;
};

int main()
{
	Myclass* ret = new Myclass;
	return 0;
}

下断点到return 0,查看控制台窗口:

监视窗口查看ret对象:

如果改动main函数为下方代码,求代码执行的结果

int main()
{
	Myclass* ret = new Myclass[5];
	return 0;
}

分析:这里的5代表对象的个数,因此会调用5次构造函数,运行结果如下:

带参的构造函数

例如以下Myclass对象:

class Myclass
{
public:
	Myclass(int data)
		:_val(data)
	{
		cout << this << ": Myclass()" << endl;
	}
	~Myclass()
	{
		cout << this <<": ~Myclass()" << endl;
	}
	int _val;
};

如果是一个对象可以直接在括号中传参:

Myclass* ret = new Myclass(1);
delete ret;

如果是多个对象,可以使用初始化列表

Myclass* ret = new Myclass[5]{ 1,2,3,4,5 };
delete[] ret;

 或者使用匿名对象:

//较新的编译器直接优化,不会构造+拷贝构造
Myclass* ret = new Myclass[5]{ Myclass(1),Myclass(2),Myclass(3),Myclass(4),Myclass(5)};
delete[] ret;

注意传参一定要完整,因为上方代码的构造函数的参数没有缺省参数,即没有默认构造

如果是传两个参数,照葫芦画瓢,使用匿名对象传多个参就行,可以这样写:

class Myclass
{
public:
	Myclass(int data,int)
		:_val(data)
	{
		cout << this << ": Myclass()" << endl;
	}
    //......
	int _val;
};

//---------------------------------------------

int main()
{
	Myclass* ret = new Myclass[2]{Myclass(1,2),Myclass(2,3)};
	delete[] ret;
	return 0;
}

或者仍然使用初始化列表

int main()
{
	Myclass* ret = new Myclass[2]{ {1,2},{2,3} };
	delete[] ret;
	return 0;
}

delete

和free不同的是,delete会调用析构函数来清理

例如以下代码:

#include <iostream>
using namespace std;
class Myclass
{
public:
	~Myclass()//这里构造函数省略
	{
		cout << "~Myclass()" << endl;
	}
	int _val;
};

int main()
{
	Myclass* ret = new Myclass;
	delete ret;
	return 0;
}

运行结果:调用了一次析构函数

同理,下方代码调用了5次析构函数

int main()
{
	Myclass* ret = new Myclass[5];
	delete[] ret;
	return 0;
}

运行结果:

new和malloc不能混用,delete和free也不能混用

这个在CC26.【C++ Cont】动态内存管理(new和delete)浅析和面向对象的方式实现链表文章中讲过,下方将证明这一点:

例如以下不规范代码1:

int main()
{
	int* ret = new int[5];
	free(ret);
	return 0;
}

运行时貌似没问题:

进程正常退出,退出代码为0

例如以下不规范代码2:

int main()
{
	Myclass* ret = new Myclass[5];
	free(ret);
	return 0;
}

编译时系统报警告:

运行时报错: 自定义类型会有问题

使用new和delete操作对象时,构造函数和析构顺序

#include <iostream>
using namespace std;
class Myclass
{
public:
	Myclass()
	{
		cout << (void*)this << ": Myclass()" << endl;
	}
	~Myclass()
	{
		cout << (void*)this <<": ~Myclass()" << endl;
	}
};

int main()
{
	Myclass* ret = new Myclass[5];
	delete[] ret;
	return 0;
}

运行结果:

按栈的规定(最先构造的对象最后析构,虽然指针指向的对象在堆上,但指针在栈上,delete操作时先找指针,因此按栈的规定)来执行构造和析构,图如下:

下篇将分析new和delete的底层实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhangcoder

赠人玫瑰手有余香,感谢支持~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值