从零开始的C++生活——第四讲

在上一讲,我向大家介绍了C++内存管理以及new/delete的相关知识,在开启这一讲之前,我想先补充一点上一讲未交代完的知识点,replacement-new相关。

由于,现阶段知识储备有限,我先简单通俗的说一说内存池的概念,不会介绍太深。当需要动态申请内存时,我们需要到堆区申请,一次两次还好,频率过高难免有些浪费时间,这时候就有内存池出现了,所谓内存池你可以理解为它本来就预留了一些向堆区申请的空间,然后你就不用再去找堆区要了,直接让内存池给你。

简单介绍过后,我来介绍我们今天要讲的定位new表达式也就是replacement-new,它用于在已分配的原始内存空间中调用构造函数初始化对象,完美契合我们内存池的前提,因为内存池就是已分配的原始内存空间。

我简单演示一下用法。

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1

#include <iostream>

using namespace std;
class A
{
public:
	A(int a = 10)
		:_a(a)
	{
		cout << "A(int a = 10)" << endl;
	}
	~A()
	{
		cout << "~A()" << endl;
	}
	void Print()
	{
		cout << _a << endl;
	}
private:
	int _a;
};
int main()
{
	A* pa = (A*)malloc(sizeof(A));
	new(pa)A(20);
	pa->Print();
	pa->~A();
	free(pa);


	return 0;
}

这段代码完美地为我们演示了什么叫多此一举......确实啊,你调用new/delete自动调用构造析构还整这么麻烦干啥呢?还是那句话,内存池可以用,现在确实展示不出来,只是为了让大家了解一下用法,显式调用构造函数一般我们也确实用不到。

那么接下来,我们正式进入第四讲模板的学习。

模板之于C++有着举足轻重的作用,以往我们写个Swap函数写个Add函数,你由于不同类型的考量需要写一堆,C++即使有重载也十分麻烦,明明大家都是干着差不多的任务,却要写好多次,代码十分冗余,模板就是来解决这个问题的。

cpp 复制代码
template<class T>
void Swap(T& t1, T& t2)
{
	T tmp = t1;
	t1 = t2;
	t2 = tmp;
}

int main()
{

	int a = 10;
	int b = 2;
	double c = 1.1;
	double d = 2.9;
	Swap(a, b);
	Swap(c, d);



	return 0;
}

这是基本的语法,template<class T>其中class你可以写typename,T这个名字你随便。

看看这段代码,思考一个问题,传double和int的这两个Swap是一个函数吗?我可以很明确的告诉你,如果你调试,进的都是这个函数,那能证明这俩是一个函数吗?

看到了吧,这其实是两个函数,也就是说,函数模板的原理是根据预定模板生成对应函数,本来这个工作是你来完成的,但是由于工作比较简单,就交给编译器了,本质不变,仍然是两个函数,只是省事且不冗余,代码观感更好。当你传入int编译器会推断出相关类型执行相应操作。由函数模板生成对应函数的过程我们称为函数模板的实例化。

实例化分为隐式实例化和显式实例化两种,上边我为大家介绍的就是隐式实例化。

什么时候隐式实例化就不够了呢?

cpp 复制代码
template<class T>
T Add(const T& t1, const T& t2)
{
	return t1 + t2;
}
int main()
{

	int a = 10;
	int b = 2;
	double c = 1.11;
	double d = 2.9;
	cout << Add(a, b) << endl;
	cout << Add(c, d) << endl;
	cout << Add(a, (int)c) << endl;
	cout << Add<int>(a, c) << endl;
	cout << Add<double>(a, c) << endl;




	return 0;
}

你想要不同类型的变量相加,这个时候既定方式不管用了,在函数模板中,一般不会对类型进行隐式类型转换,所以要么就你手动转换类型要么就显式实例化。

还有一种情况,你必须显式实例化,当你没有传泛型参数的时候,编译器怎么知道你要用什么类型,所以你必须指定。

上述讲的都是只有一个模板参数的情况,但是现实中有多个模板参数的情况还是很常见的,毕竟你不能保证在一个函数里一直只用一种变量类型吧。

最简单的情况,Add可以实现一个整型加一个浮点数吧,你来回显式实例化,也很麻烦。

最后介绍一下函数模板的一些规则。

一个非模板函数可以和一个同名的函数模板同时存在,而且模板函数还可以实例化为该非模板函数。

如果非模板函数和模板函数其他条件都相同优先调用非模板函数。

模板函数内部不允许自动类型转换。

介绍完函数模板之后,还有类模板,接下来我介绍一下类模板。

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

	}
	void Push(const T& t);
private:
	T* _parr;
	int _capacity;
	int _size;

};
template<class T>
void Stack<T>::Push(const T& t)
{
	if (_capacity == _size)
	{
		//手动扩容
		T* tmp = new T[2 * _capacity];
		memcpy(tmp, _parr, sizeof(T) * _size);
		delete[] _parr;
		_parr = tmp;
		_capacity *= 2;
	}
	_parr[_size] = t;
	_size++;

}

int main()
{

	Stack<int> s1;
	Stack<double> s2;




	return 0;
}

和函数模板不同的是,类模板必须要显式实例化,和它类似的是,Stack<int>和Stack<double>不是同一种类型。

值得注意的是,C++需要手动扩容,但是现在没办法说为什么,请期待后续讲解。

相关推荐
云和数据.ChenGuang1 小时前
Python 集合(set)常用函数详解与实战案例
开发语言·python·深度学习·langchain
隔窗听雨眠1 小时前
从PHP2Shell回望:PHP内核漏洞的二十年演进与AI时代的攻防转折
开发语言·人工智能·php
lisw051 小时前
分岔论揭示演化突变密码
开发语言·算法·数学建模
hunteritself1 小时前
AI 挖出 PHP「致命漏洞」!欧洲政府连夜关站,日本首相紧急喊话
开发语言·人工智能·php
努力实干2 小时前
Qt/C++ 模块化设计与工程组织
开发语言·c++·qt
Rabitebla2 小时前
【Linux底层原理】 工具篇(二):Vim 编辑器基础操作与简单配置
linux·c++·编辑器·vim
阿里嘎多学长2 小时前
2026-10-23 GitHub 热点项目精选
开发语言·程序员·github·代码托管
Lhan.zzZ2 小时前
Qt:动态库模块框架设计
开发语言·c++·qt
quantdash_cc2 小时前
如何从多只股票的历史行情构建收益率矩阵?Python 与 QuantDash 实战教程
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash