C++模板初阶

发布于:2025-07-29 ⋅ 阅读:(13) ⋅ 点赞:(0)

希望文章能对你有所帮助,有不足的地方请在评论区留言指正,一起交流学习!

1. 泛型编程

        在C语言中由于数据类型的不同,导致需要对相同逻辑的函数编写多份,例如交换函数Swap函数;在交换的时候,需要相同类型数据才可以交换。如下:
        
#include <iostream>
using namespace std;

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;
}
void Swap(char& left, char& right)
{
	char temp = left;
	left = right;
	right = temp;
}
// 日期类
class Date
{
public:

	Date(int year = 2025, int month = 7, int day = 28)
		:_year(year)
		,_month(month)
		,_day(day)
	{}
	 void Swap(Date&d)
	{
		// 为了简化,直接调用的C++库中的交换函数 swap
		swap(_year, d._year);
		swap(_month, d._month);
		swap(_day, d._day);
	}
	 void Print()
	 {
		 cout << _year << "-" << _month << "-" << _day << endl;
	 }
private:
	size_t _year;
	size_t _month;
	size_t _day;
};

int main()
{

	// 自定义类型
	Date d1(2024, 8, 6);
	Date d2;
	cout << "交换前" << endl;
	d1.Print();
	d2.Print();
	d1.Swap(d2);
	//swap(d1, d2);
	cout << "交换后" << endl;
	d1.Print();
	d2.Print();

	// 内置类型
	int a1 = 10; int a2 = 20;
	cout << "交换前" << a1 << " " << a2 << endl;
	Swap(a1,a2);
	cout << "交换后" << a1 << " " << a2 << endl;

	double b1 = 22.22; double b2 = 33.33;
	cout << "交换前" << a1 << " " << a2 << endl;
	Swap(b1, b2);
	cout << "交换后" << a1 << " " << a2 << endl;
	
	return 0;
}

        上述代码中的每种数据类型就有一种交换函数,而且C++是面向对象的代码,其中的类型更多,因此引入了泛型编程的概念。

        泛型编程是一种编程范式,它允许在不指定具体数据类型的情况下编写代码,从而实现代码复用类型安全的平衡。在 C++ 中,泛型编程主要通过模板(Templates)实现,包括函数模板类模板。   

2. 函数模板

(1)函数模板介绍

        函数模板是一种能创建通用函数的工具,它可以处理不同的数据类型。借助模板参数,函数模板能够对类型进行参数化,从而实现代码的复用。        

        函数模板格式
template <typename T>
// template <typename T>
返回类型 函数名(参数列表) {
    // 函数体
}

        T就是模板参数,它代表着一种通用类型。在函数体中,T可以当作普通数据类型来使用。

实例:

#include <iostream>
using namespace std;
template <typename T>
void Swap( T& x,  T& y)
{
	T tmp = x;
	x = y;
	y = tmp;

}
template <typename T>
void Func(const T& x, const T& y)
{
	cout << x << " " << y << endl;
}
int main()
{
	int a = 6;
	int b = 9;
	Func(a, b);
	Swap(a, b);
	Func(a, b);

	double c = 5.67;
	double d = 8.97;
	Func(c, d);
	Swap(c, d);
	Func(c, d);
	return 0;
}

        上述程序存在两个模板,一个模板是交换函数模板,一个输出函数模板,使用的都是一种数据类型的模板,那么多个数类型如何操作,如下

#include <iostream>
using namespace std;

template <typename T1, typename T2>
T1 Add(T1& x, T2& y)  // 返回的是T1类型的数据,可以将T1/T2理解为万能的数据类型
{
	return x + y;
}
int main()
{
	int a = 2;
	double b = 5.67;
	cout << Add(a, b) << endl;
	cout << Add(b, a) << endl;
	return 0;
}

        两种类型的模板函数,其返回值是根据第一个传递过去的值来确定的,那么在定义一个模板参数的是什么情况呢?

(2)函数模板原理

        函数模板是一种参数化的函数定义,它本身并不是一个具体的函数,而是创建具体函数的蓝图。在编译器编译阶段,对于模板函数的使用,编译器会根据传入的实参类型来推演生成对应类型的函数以供调用。

        上述汇编语言采用的是交换函数中int double类型,编译器会根据传递的类型,编写不同类型的函数。

(3)模板实例化

        模板实例化是 C++ 编译过程中将函数模板或类模板转换为具体函数或类的核心机制。分为隐式实例化和显示实例化。

隐式实例化让编译器根据实参推演模板参数的实际类型;如下:

template <typename T>
T Add(T& x, T& y)  // 返回的是T1类型的数据,可以将T1/T2理解为万能的数据类型
{
	return x + y;
}
int main()
{
	int x1 = 5;
	int y1 = 8;
	cout << Add(x1, y1) << endl;

	double x2 = 5.7;
	double y2 = 8.9;
	cout << Add(x2, y2) << endl;

	
	return 0;
}
cout << Add(x1, y2) << endl;

上述代码中只有一种参数类型模板,但是匹配的是两种数据类型,一种方法就是强制转换如下:

    cout << Add((double)x1, y2) << endl; //强制转换会产生中间变量(具有常性),形参加上const
	cout << Add(x1, (int)y2) << endl;

还有一种方法就是显示实例化,直接告诉编译器,要转换那种类型。

显示实例化

    cout << Add<int>(x1, y2) << endl;
	cout << Add<double>(x1, y2) << endl;

一般情况下均是隐式实例化。

(4)模板参数的匹配原则

  1. 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。

  2. 如果模板可以产生一个具有更好匹配的函数, 那么将选择模板。一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数。

  3. 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换;也就是不能存在隐式类型转换。

3. 类模板

        类模板是 C++ 泛型编程的核心机制,允许创建通用类以支持多种数据类型。
(1)模板形式
template<class T1, class T2, ..., class Tn>
class 类模板名
{
 // 类内成员定义
};

以栈为例:

template <typename T>
class Stack
{
public:
	Stack(int capacity = 10)
		:_a((T*)malloc(sizeof(T)*capacity))
		, _size(0)
		, _capacity(capacity)
	{}



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

        上述代码只是栈类的一部分,想要完全实现栈的模板,需要将其中的所有类型都改为T模板参数

(2)模板实例化
        类模板只能显示实例化。如下:
    Stack<int> st1;
	Stack<double> st2;
	Stack<char> st3;

注意: 模板参数的作用范围是当前函数或者当前类,跟着域走或者花括号;一个模板参数的生命周期就是一个函数模板或者一个类模板.

(3)成员函数的声明和定义
        和普通类不同的时候,将函数定义在类外部的情况是不同。模板使用 类名+<T>代替普通模板类的类名。
        模板类是没有类型的,只有在实例化之后才有类型例如Stack<int>.
        普通类的类型和类名是一样的,模板类的类名和普通类一样,但是类型不同如上。
实例:模板类的定义方式
//简单一些的函数为例子
template <typename T>
void Stack<T>::Print(const T& st)
{
	cout << st._size << endl;
}


网站公告

今日签到

点亮在社区的每一天
去签到