C++ 内存管理 / 模板

new&delete

1.发明缘由

在C语言中,我们在堆上申请空间,使用的是malloc,calloc,和realloc。释放空间是用free。
而C++因为有类,如果只使用malloc和free就无法匹配,有些功能无法实现。所以C++发明了new 和 delete。new对应malloc等delete对应free

2.语法

int main()
{
	//C++
	int *p2 = new int;
	delete p2;

	//C++的申请空间可以直接初始化
	int *p3 = new int(1);
	delete p3;

	int *p4 = new int[10]{ 1,2,3,4 };
	delete[] p4;
}

new后跟空间的类型。多个用类似数组的形式,用[]指定对象。
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.适用场景

因为C++有类的存在,并且类中的构造函数和析构函数,是完成对对象的初始化和释放的,所以C++需要有相匹配的开辟空间和释放空间的方式,才不会造成内存泄漏。
我们举个栈的例子

这是C语言建立栈和销毁栈
在这里插入图片描述

这是C++的
在这里插入图片描述

所以malloc和free,new和delete最好匹配使用,不然可能会出现莫名其妙的报错

5.小细节

实际使用new,会调用operator new函数,注意:operator new不是重载new,是这个函数的名称就叫这个。
operator new内部也有调用malloc,是一个封装行为。申请多个空间会先调用operator new[],其内部再调用operator new。
delete也是一样,会调用operator delete,内部有调用free。

模板

模板的关键字是template

1.函数模板

一. 发明缘由

我们知道,为了实现泛式编程,C++引入了函数重载,使得同一个名称的函数可以实现对不同数据类型的操作,但是函数重载仍需要我们编写重复的代码。
比如,交换两个变量的Swap函数

void Swap(int& left, int& right)
{
	int temp = left;
	left = right;
	right = temp;
}

void Swap(double& left, double& right)
{
	double temp = left;
	left = right;
	right = temp;
}

一个类型就需要写一个函数。有什么办法可以简化这些代码呢?
那么就需要模板了。

二. 语法

关键字template<class 数据名>
class也可以换成typename,此处无差别
数据名也可以多个,通常是大写

template<class T>
T Swap(T& x, T& y)
{
	T tmp = x;
	x = y;
	y = tmp;
}

注意:传入不同的参数,调用的函数并不相同

int main()
{
	int a = 1, b = 2;
	double c = 1.5, d = 2.7;
	Swap(a, b);
	Swap(c, d);

	return 0;
}

在这里插入图片描述
在这里插入图片描述
其实,这里也是使用函数重载,只不过不是我们编写,而是编译器帮我们写

在这里插入图片描述

显示实例化
除了让编译器自动推演类型,我们还可以直接告诉编译器实例化的类型

Swap<int>(a,b);

三. 小细节

在这里插入图片描述
这两个函数可以同时存在,如果调用,会优先匹配第一个。因为匹配第二种,编译器也还是会进行函数重载,这个时候编译器会发现第一个是匹配的,就直接使用,节省时间。

编译器也会自己匹配最优的函数

void Swap(int& x, int& y)
{
	int tmp = x;
	x = y;
	y = tmp;
}

template<class T>
void Swap(T& x, T& y)
{
	T tmp = x;
	x = y;
	y = tmp;
}

template<class T,class Y>
void Swap(T& x, Y& y)
{
	T tmp = x;
	x = y;
	y = tmp;
}

int main()
{
	int a = 1, b = 2;
	double c = 1.5, d = 2.7;
	Swap(a, d);

	return 0;
}

即使还有另外两个Swap函数,但是一个int,一个double,编译器会匹配更好的最后一个
在这里插入图片描述

2.类模板

同函数模板,假如我们要实现栈,但是需要处理不同数据类型的栈,那么typedef StDateType int显然无法解决。所以需要类模板

template<class T>
class Stack
{
public:
	Stack(int capacity)
	{
		_a = new T[capacity];
		_capacity = capacity;
		_top = 0;
	}

	~Stack()
	{
		delete[] _a;
		_a=nullptr;
		_capacity = _top = 0;
	}

private:
	T* _a;
	size_t _top;
	size_t _capacity;
};

int main()
{
    //因为传参和模板的T无关,所以需要显示实例化
	Stack<int> st1(4);//int
	Stack<double> st2(5.5);//double
}

注意:因为传参和模板的T无关,所以需要显示实例化

如果成员函数的定义写在函数外
则需再写一遍模板,并且作用域的写法也有所不同
例如将上述的Stack的析构函数写在类外

template<class T>

Stack<T>::~Stack()
{
   delete[] _a;
	_a=nullptr;
	_capacity = _top = 0;
}

本章用于记笔记,如果有不对或者不足的地方,欢迎大佬们指正,补充。感谢大家的阅读,如果感觉博主写的还可以,麻烦点个赞支持一下,阿里嘎多。

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值