006C++ 内存管理

前言

本文将会向您介绍C/C++内存管理方式,new与delete的原理与用法

一、C与C++内存管理方式

C语言使用malloc/calloc/realloc/free函数来进行内存管理

int main()
{
	int* p1 = (int*)malloc(sizeof(int) * 10);
	//检查是否开辟失败
	if (p1 == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	//初始化
	*p1 = 1;
	//释放
	free(p1);
	p1 = NULL;
	return 0;
}

如以上程序,在C语言中开辟空间时,我们不仅要手动sizeof计算大小还要对指针进行强转,另外还需要检查指针的合法性,还不能在开辟空间时进行初始化。
对于面向对象的C++,使用C的方式开辟空间不仅仅是不太好用,还满足不了一些地方的需求
如:以下是一个Stack类

typedef char DataType;
class Stack
{
public:
	Stack(size_t capacity = 4)
	{
		cout << "Stack" << endl;
		_capacity = capacity;
		_array = new DataType[capacity];
		//_array = new char[0x7fffffff];
		_size = 0;
	}
	void Push(DataType data)
	{
		_array[_size] = data;
		_size++;
	}

	~Stack()
	{
		cout << "~Stack()" << endl;
		delete[] _array;
		_array = nullptr;
		_size = _capacity = 0;
	}
public:
	//内置类型
	DataType* _array;
	size_t _capacity;
	size_t _size;
};
int main()
{
	Stack* p1 = (Stack*)malloc(sizeof(Stack) * 10);
	return 0;
}

malloc不会调用类的构造函数,也无法正确地初始化类的成员变量
因此C++使用new与delete操作符进行动态内存管理

二、new与delete

1. new与delete

如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。
new的原理
自定义类型

1、调用operator new函数申请空间在申请的空间上执行构造函数,完成对象的构造

delete的原理

1、在空间上执行析构函数,完成对象中资源的清理工作调用
2、operator delete函数释放对象的空间

new T[N]的原理

1、调用operator new[]函数,在operator new[]中实际调operator new函数完成N个对象空间的申请
2、在申请的空间上执行N次构造函数

delete[]的原理

1、在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
2、调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

格式(仅供参考):

T* p = new T
delete p

//初始化
T* p = new T()
delete p
 
//多个对象
T* p = new T[ ]
delete[] p

//多个对象+初始化
T* p = new T[]{}
delete[] p
new:操作符
T:类型
():初始化
[]:对象个数
{}:初始化
int main()
{
       //自定义类型,开空间,调用构造函数初始化
       A* p2 = new A;
       A* p3 = new A(3);
       //自定义类型。调用析构函数 + 释放空间
       delete p2;
       delete p3;
 }

在这里插入图片描述

class A
{
public:
	A(int a = 0)
		:_a(a)
	{}
	~A()
	{
		cout << "~A" << endl;
	}
private:
	int _a;
};
int main()
{
       A* p4 = new A[10];
       delete[] p4;
       A aa1(1);
       A aa2(2);
       A* p5 = new A[10]{ aa1,aa2 };
       delete[] p5;
       //用匿名对象初始化
       A* p6 = new A[10]{ A(1),A(2) };
       delete[] p6;
       //隐式类型转换
       A* p7 = new A[10]{ 1,2 };
       delete[] p7;
}

2. 抛异常

malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常

void func()
{
	Stack st;
	char *p1 = new char[0x7fffffff];
	cout << (void*)p1 << endl;
}

int main()
{
	try 
	{
		func();
		cout << "hello" << endl;
 	}
 	//捕获异常
	catch (const exception& e)
	{
		cout << e.what() << endl;
	}
}

若是在try块中抛出了异常,那么异常后的代码将不会继续执行
程序会立刻跳转至catch块

3. operator new与operator delete

operator new 与 operator delete 时系统提供的全局函数,new在底层调用operator new 全局函数来申请空间,delete在底层用operator delete全局函数来释放空间

int main()
{
	int* p1 = new int;
	delete p1;
}

006C++ 内存管理_第1张图片

Stack* pst1 = (Stack*)operator new(sizeof(Stack));
operator delete(pst1);	
另外我们可以通过反汇编观察到operator new、operator delete的底层。其实operator new 与 operator delete就是封装的malloc与free

006C++ 内存管理_第2张图片
006C++ 内存管理_第3张图片

4. malloc/free 和 new/delete的区别

通过以上内容我们可以总结出:

new :operator new(封装的malloc) + 构造函数(内置类型不会调用)
delete:operator delete(封装的free) + 调用析构函数(内置类型不会调用)

我们来看以下程序

class A
{
public:
	A(int a = 0)
		:_a(a)
	{}
	~A()
	{
		cout << "~A" << endl;
	}
private:
	int _a;
};
	内置类型
	int* p1 = new int[10];
	free(p1);
	delete p1;
	delete[] p1;

当类型为内置类型的时候,以上三种释放空间的方式都不会报错,原因是delete在底层调用operator delete(封装的free)和调用析构函数(内置类型不会调用)

	A* pst2 = new A[10];
	//delete pst2;
	//free(pst2);
	delete[] pst2;
当类型为自定义类型,我们就必须要匹配进行使用了

malloc/free 和 new/delete的区别:

1.malloc和free是函数,new和delete是操作符
2.malloc申请的空间不会初始化,new可以初始化
3.malloc申请空间时,需要手动计算空间大小并传递,new仅需要在其后跟上空间的类型即可,如果十多个对象,[ ] 中指定对象个数即可
4.malloc的返回值为ivoid* 在使用时必须强转,new不需要,因为new后跟的时空间的类型
5.malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
6.申请自定义类型对象时,malloc/free只会开辟与释放空间,不会调用构造与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理

三、内存分布

我们先看以下程序

int globalVar = 1;
static int staticGlobalVar = 1;
int main()
{
int num[] = {1, 2, 3, 4};
char char2[] = "abcd";
const char* pchar3 = "abcd";
int* ptr1 = (int*)malloc(sizeof(int) * 4);
int* ptr2 = (int*)calloc(4, sizeof(int));
int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
free(ptr1);
free(ptr3);
}

选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)
globalVar在哪里?C
staticGlobalVar在哪里?C
staticVar在哪里?C
localVar在哪里?A
num1 在哪里?A

char2在哪里?A
*char2在哪里?A
pChar3在哪里 ? A
*pChar3在哪里?D
ptr1在哪里?A
*ptr1在哪里?B

006C++ 内存管理_第4张图片
注意:char2是数组名,是数组首元素的地址,存储在栈上,
*char2,解引用char2得到数组首元素a,数组在栈上

char char2[] = "abcd";

这句代码的含义是用”abcd“这个常量字符串初始化char2
常量字符串属于代码段
而pchar3是一个指向常量字符串的指针,存储在栈上
指针存储常量字符串的地址,解引用指针得到常量字符串
006C++ 内存管理_第5张图片

小结

今天就到这里啦,如果本文存在疏漏或错误的地方,还请您能够指出。

你可能感兴趣的:(Fan——C++,c++)