【C++】内存管理与模板(C++内存管理方式、new和delete的实现原理、malloc/free和new/delete的区别、函数模板、类模板)

小编主页详情<-请点击

小编gitee代码仓库<-请点击


本文主要介绍了内存管理与模板(C++内存管理方式、new和delete的实现原理、malloc/free和new/delete的区别、函数模板、类模板),内容全由作者原创(无AI),并带有配图帮助博友们更好的理解,点个关注不迷路,下面进入正文~~


目录

1.C++内存管理方式

1.1new/delete操作内置类型

[1.2 new和delete操作自定义类型](#1.2 new和delete操作自定义类型)

[1.3operator new与operator delete函数](#1.3operator new与operator delete函数)

[2. new和delete的实现原理](#2. new和delete的实现原理)

2.1内置类型

2.2自定义类型

[3. malloc/free和new/delete的区别](#3. malloc/free和new/delete的区别)

4.函数模板

5.类模板

结语:


1.C++内存管理方式

在C语言中,我们会使用malloc开辟空间,用free销毁空间,在C++中我们依旧可以使用。但是在某些情况下譬如类的初始化和销毁会显得很乏力。因此C++又引入了新的管理内存的方式:通过newdelete进行动态内存管理。

1.1new/delete操作内置类型

cpp 复制代码
int main()
{
	int* p1 = new int;
	// 动态申请一个int类型的空间
	int* p2 = new int[10];
	// 动态申请10个int类型的空间
	delete p1;
	delete[] p2;

	// +
	int* p3 = new int(0);
	// 动态申请一个int类型的空间并初始化为0
	int* p4 = new int[10]{ 0 };
	// 动态申请10个int类型的空间并初始化为0
	int* p5 = new int[10]{1,2,3,4,5};
	// 动态申请10个int类型的空间并初始化为1,2,3,4,5
	delete p3;
	delete[] p4;
	delete[] p5;

	return 0;
}

注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用new\[\]和delete\[\],注意:匹配起来使用。

1.2 new和delete操作自定义类型

在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与free不会。因此,在操作自定义类型时,我们会更推荐用new/delete实现,毕竟能自动调用构造函数和析构函数还是相当方便的。

cpp 复制代码
class A
{
public:
	A(int a = 0)
		: _a(a)
	{
		cout << "A():" << this << endl;
	}
	~A()
	{
		cout << "~A():" << this << endl;
	}
private:
	int _a;
};
int main()
{
	// new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间
	// 还会调用构造函数和析构函数
	A* p1 = (A*)malloc(sizeof(A));
	A* p2 = new A(1);
	free(p1);
	delete p2;
	// 内置类型是几乎是一样的
	int* p3 = (int*)malloc(sizeof(int)); // C
	int* p4 = new int;
	free(p3);
	delete p4;
	A* p5 = (A*)malloc(sizeof(A) * 10);
	A* p6 = new A[10];
	free(p5);
	delete[] p6;
	return 0;
}

1.3operator new与operator delete函数

new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。

operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间失败,尝试执行空间不足应对措施,如果该应对措施用户设置了,则继续申请,否则抛异常。operator delete 最终是通过free来释放空间的。

总的来说,operator new和operator delete本质上都是通过调用malloc和free来进行开辟和释放空间的。

2. new和delete的实现原理

2.1内置类型

如果申请的是内置类型的空间,new/malloc和delete/free其实差距不大。需要注意的是,new/delete申请和释放的是单个元素的空间,而new\[\]和delete\[\]申请和释放的是连续空间,并且new在申请空间失败时会抛异常,而malloc申请空间失败会返回NULL。

2.2自定义类型

new的原理:

1.调用operate new函数申请空间

2.在申请的空间上调用构造函数,完整对象的构造
delete的原理:

1.在空间上执行析构函数,完成对象的资源清理工作

2.调用operate delete函数释放对象的空间
new TN的原理

  1. 调用operator new\[\]函数,在operator new\[\]中实际调用operator new函数完成N个对象空间的申请

  2. 在申请的空间上执行N次构造函数
    delete\[\]的原理

  3. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理

  4. 调用operator delete\[\]释放空间,实际在operator delete\[\]中调用operator delete来释放空间

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

malloc/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。不同的地方是:

  1. malloc和free是函数,new和delete是操作符

  2. malloc申请的空间不会初始化,new可以初始化

  3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,如果是多个对象,\[\]中指定对象个数即可

  4. malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型

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

  6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理释放

4.函数模板

在我们进行两个数据swap交换时,会因为需要交换的数据类型有很多种,导致我们需要写很多种数据类型的swap交换函数。函数模板可以通过传入数据的类型,生成对应类型的函数。

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
template<class T>
void Swap(T& left, T& right)
{
	T tmp = left;
	left = right;
	right = tmp;
}

int main()
{
	int a = 1;
	int b = 2;

	Swap(a, b);
	cout << a << " " << b << endl;

	double c = 1.1;
	double d = 2.2;

	Swap(c, d);
	cout << c << " " << d << endl;
	return 0;
}

如这段代码,当我们传入int类型数据时,编译器会判断T的类型就是int;当我们传入double类型数据时,编译器会判断T的类型就是double。此时编译器会自动生成参数分别位int和double的函数,本质上就是把我们要写多种相似代码的工作交给了编译器。

当然函数模板参数可以有很多个,比如T1、T2、T3

下面是函数模板的格式:

cpp 复制代码
template<typename T1, typename T2,......,typename Tn>

typename是用来定义模拟参数的关键字,也可以使用class

当模板参数只有一个,我们传入两个不一样的参数,编译器无法判断T的类型是什么,就会报错。

那如果我们就是想在模板参数只有一个时让编译器不报错要怎么做呢?

1.用户自己强制类型转化

2.使用显示实例化

cpp 复制代码
// 用函数模版生成对应的函数 -> 模版的实例化
template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}

template<class T1, class T2>
T1 Add(const T1& left, const T2& right)
{
	return left + right;
}

template<class T>
T* func1(int n)
{
	return new T[n];
}


int main()
{
	int a1 = 10, a2 = 20;
	double d1 = 10.1, d2 = 20.2;
	Add(a1, a2);
	Add(d1, d2);

	// 推导实例化
	cout << Add(a1, (int)d1) << endl;
	cout << Add((double)a1, d1) << endl;

	// 显示实例化
	cout << Add<int>(a1, d1) << endl;
	cout << Add<double>(a1, d1) << endl;

	//用两个模板参数实现
	cout << Add(a1, d1) << endl;

	double* p1 = func1<double>(10);

	return 0;
}

当一个非模板函数和模板函数同时存在时,如果类型匹配,会优先调用非模板函数。

5.类模板

类模板和函数模板的使用其实没有什么区别,主要的区别是类模板只能显示实例化,不然无法判断T的类型是什么。

类模板的定义格式如下:

cpp 复制代码
template<class T1, class T2, ..., class Tn> 
class // 类模板名
{
    // 类内成员定义
};   

下面是用类模板定义的一个栈

cpp 复制代码
template<typename T>
class Stack
{
public:
	Stack(int n = 4)
		:_array(new T[n])
		,_size(0)
		,_capacity(n)
	{}

	~Stack()
	{
		delete[] _array;
		_array = nullptr;
		_size = _capacity = 0;
	}

	void Push(const T& x);

private:
	T* _array;
	size_t _capacity;
	size_t _size;
};

template<class T>
void Stack<T>::Push(const T& x)
{
	if (_size == _capacity)
	{
		T* tmp = new T[_capacity * 2];
		memcpy(tmp, _array, sizeof(T) * _size);
		delete[] _array;

		_array = tmp;
		_capacity *= 2;
	}

	_array[_size++] = x;
}


int main()
{
	// 类模板都是显示实例化
	Stack<int> st1; // int
	st1.Push(1);
	st1.Push(2);
	st1.Push(3);

	Stack<double> st2; // double
	st2.Push(1.1);
	st2.Push(1.1);
	st2.Push(1.1);

	Stack<double>* pst = new Stack<double>;
	//...
	delete pst;

	return 0;
}

模版不建议声明和定义分离到两个文件.h 和.cpp会出现链接错误,具体原因后面会讲

Stack是类名,Stack<int>才是类型

结语:

这篇文章全文由作者手写,图片由画图软件所制,无AI制作,希望各位博友能有所收获

欢迎各位博友的讨论,觉得不错的小伙伴,别忘了点赞关注哦~

相关推荐
雪的季节1 小时前
Qt多窗口架构设计需求简介
开发语言·qt
zlinear数据采集卡1 小时前
模拟输入限流保护电路深度解析:从理论原理到ZLinear采集卡的实战设计
c语言·单片机·嵌入式硬件·fpga开发·自动化
j7~1 小时前
MySQL C语言连接库和MYSQL连接池原理与简易数据网站数据流动是如何进行的
c语言·数据库·mysql·连接池·mysqlc语言连接库
韦胖漫谈IT1 小时前
面向对象 vs 函数式背后的思维差异
开发语言
SoftLipaRZC1 小时前
C语言自定义类型:联合和枚举完全指南
c语言·算法
Xin_ye100861 小时前
C# 零基础到精通教程 - WPF 深度专题:3D 图形与视觉增强
开发语言·c#·wpf
zhangfeng11332 小时前
台大李宏毅老师讲解memba和类似linear atttenion 模型,笔记
开发语言·人工智能·笔记
Chris _data3 小时前
并发单词频率统计器 - 从零到完整实现(C# 实战)
开发语言·c#
idolao3 小时前
Oligo 7.60 安装教程:引物设计+Java 环境配置
java·开发语言