前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【C++】初阶模板

【C++】初阶模板

作者头像
修修修也
发布2024-04-20 08:56:40
640
发布2024-04-20 08:56:40
举报

泛型编程

模板是C++泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。 模板是创建泛型类或函数的蓝图或公式。库容器,比如迭代器和算法,都是泛型编程的例子,它们都使用了模板的概念。 每个容器都有一个单一的定义,比如 向量,我们可以定义许多不同类型的向量,比如 vector <int>vector <string>。 我们可以使用模板来定义函数和类,接下来让我们一起来看看如何使用。

函数模板

很多时候,我们在编程时会遇到这样的情形,如在通讯录程序中,我们想要实现两个联系人的信息互换,如我们要将张三和李四除了姓名之外的所有信息做交换:

这个时候因为交换的数据类型并不相同,就需要我们编写很多Swap交换函数来完成这一功能,如:

仔细观察可以发现,这三个Swap交换函数除了参数类型不同,其余的函数逻辑是一模一样的,那么有没有一种方法可以简化这种重复又烦琐的工作呢? 答案是有的,接下来为大家介绍一下函数模板:

函数模板的定义

模板函数定义的一般形式如下所示:

代码语言:javascript
复制
template <typename T> 
ret_type func_name(parameter list)
{
	// 函数主体
}

其中,各个名称所代表的含义如下图:

了解了模板函数定义的一般形式后,我们尝试套用一下前面提到的Swap的案例:

代码语言:javascript
复制
template <typename T>
        //此处typename也可换为class
void Swap(T& x, T& y)
{
	T temp = x;
	x = y;
	y = temp;
}

再编写一个主函数程序测试一下函数模板的功能:

代码语言:javascript
复制
int main()
{
	int a = 3, b = 5;
	double c = 3.3, d = 5.5;
	char ch1 = 'e', ch2 = 'f';

	Swap(a, b);
	Swap(c, d);
	Swap(ch1, ch2);

	cout << a << " - " << b << endl;
	cout << c << " - " << d << endl;
	cout << ch1 << " - " << ch2 << endl;

	return 0;
}

测试结果如下图,可以看到,函数模板成功完成了不同的类型数据的交换工作:

函数模板的实现原理

对于函数模板,还有一个值得我们思考的问题:

答案是否定的,它们调用的并不是同一个函数,这点可以通过查看汇编的方法来验证模板的底层实现:

也就是说,函数调用的其实是函数模板生成的具体的函数.由模板生成具体函数的这一过程也被称为模板的实例化:

在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此.

函数模板定义多个模板参数

函数模板的模板参数也可以定义多个,如:

代码语言:javascript
复制
template <typename T1, typename T2>
void Print(T1& x, T2& y)
{
	cout << x << " - " << y << endl;
}

int main()
{
	int a = 3;
	double b = 1.35;
	char ch = 'A';

	Print(a, b);
	Print(a, ch);
	Print(b, ch);

	return 0;
}

测试运行结果如下:

函数模板的实例

用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:隐式实例化和显式实例化。

隐式实例化

让编译器根据实参推演模板参数的实际类型.

代码语言:javascript
复制
template<class T>
T Add(const T& x, const T& y)
{
	return x + y;
}

int main()
{
	int a1 = 10, a2 = 20;
	double d1 = 10.0, d2 = 20.0;
	//正常隐式实例化调用
	Add(a1, a2);
	Add(d1, d2);

	//错误隐式实例化调用
	Add(a1, d1);
	/*
	该语句不能通过编译,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型
	通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有一个T,
	编译器无法确定此处到底该将T确定为int 或者 double类型而报错
	注意:在模板中,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就需要背黑锅
	*/

	// 此时有两种处理方式:1. 用户自己来强制转化 2. 使用显式实例化
	Add(a1, (int)d1);

	return 0;
}

显式实例化

在函数名后的<>中指定模板参数的实际类型.

代码语言:javascript
复制
template<class T>
T Add(const T& x, const T& y)
{
	return x + y;
}

int main(void)
{
	int a = 10;
	double b = 20.0;
	// 显式实例化
	Add<int>(a, b);
	return 0;
}

显示实例化的实际应用场景,如下代码:

代码语言:javascript
复制
template<typename T>
T* Alloc(int n)
{
	return new T[n];
}

int main()
{
	double* pd = Alloc(10);

	return 0;
}

因为模板无法根据参数隐式推导出返回值的类型,因此就只能通过显示实例化来完成这一功能:

代码语言:javascript
复制
template<typename T>
T* Alloc(int n)
{
	return new T[n];
}

int main()
{
	double* pd = Alloc<double>(10);

	return 0;
}

类模板

正如我们定义函数模板一样,我们也可以定义类模板。泛型类声明的一般形式如下所示:

代码语言:javascript
复制
template <class T1,class T2,...,class Tn> 
class class_name 
{
    //类的相关定义
}

//类的实例化
class_name<T1,T2,...,Tn> object_name

在这里,T 是占位符类型名称,可以在类被实例化的时候进行指定。可以使用一个逗号分隔的列表来定义多个泛型数据类型。

类模板的主要用途是当我们想要在一个项目文件中创建可以存储不同数据类型的类,比如我们需要三个栈,一个存储整形数据,一个存储浮点型数据,一个存储字符型数据,这时候创建3个仅数据类型不同的栈类就很麻烦,因此我们可以选择使用类模板来完成这项工作.代码如下:

代码语言:javascript
复制
//类模板
template<class T>
class Stack
{
public:
	Stack(size_t capacity = 3)
	{
		_array = new T[capacity];
		_capacity = capacity;
		_size = 0;
	}

	void Push(const T& data)
	{
		//CheckCapacity();
		_array[_size] = data;
		_size++;
	}

	//其他方法...

	~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}

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

int main()
{
	//存储整形数据的栈sti
	Stack<int> sti();

	//存储单精度浮点型数据的栈stf
	Stack<float> stf();

	//存储字符型数据的栈stc
	Stack<char> stc();

	return 0;
}

类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类.

代码语言:javascript
复制
// Vector类名,Vector<int>才是类型
Vector<int> s1;
Vector<double> s2;

结语

希望这篇关于 C++模板 的博客能对大家有所帮助,欢迎大佬们留言或私信与我交流.

学海漫浩浩,我亦苦作舟!关注我,大家一起学习,一起进步!

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2024-04-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 泛型编程
  • 函数模板
    • 函数模板的定义
      • 函数模板的实现原理
        • 函数模板定义多个模板参数
          • 函数模板的实例
          • 类模板
          • 结语
          相关产品与服务
          对象存储
          对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档