学无先后,达者为师

网站首页 编程语言 正文

C++函数模板和类模板详解

作者:小圣编程 更新时间: 2022-07-18 编程语言

泛型编程在实际当中运用很广泛,它可以不针对某种类型 (例如 int、double、float 等),能适应广泛的类型,在很多需要重复编写的代码当中可以很大程度上减少程序的代码量,提高效率

我们先思考一下,如何实现一个通用的交换函数呢?

//可以使用之前的函数重载的知识完成
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;
}

有没有发现每次增加新的类型就要多添加程序代码,很麻烦

在我们古代,活字印刷可以将经常使用的文字刻出来方便下次使用,同样,我们要交换两个数据的多种类型,就可以使用交换的函数模板

函数模板的使用

使用格式:

template<typename T1>//1个模板参数
template<typename T1, typename T2,......,typename Tn>//也可以多个

我们用函数模板再写一下上面的交换函数

//用法
template<typename T>//在交换函数上面写
void Swap(T& left, T& right)
{
	T temp = left;
	left = right;
	right = temp;
}

int main()
{
	int a = 0, b = 1;
	double c = 2.2, d = 3.2;

	Swap(a, b);
	Swap(c, d);
	cout << a << " " << b << endl;//打印 1 0
	cout << c << " " << d << endl;//打印 3.2 2.2 
	return 0;
}

函数模板大部分还会自动推导转换的类型,这时我们只需写一个交换函数,就可以实现多种类型的交换,很方便

函数模板的实例化

//用法
template<class T>
T Add(const T& left, const T& right)
{
  return left + right;
}
int main()
{
  int a1 = 10, a2 = 20;
  double d1 = 10.0, d2 = 20.0;
  Add(a1, a2);//隐式实例化 都是int 会自动推导int
  Add(a1, d2);//这时两个类型不同 
  Add<int>((a1, (int)d2);//显示实例化 我们可以指定算的类型
  return 0;
}

模板参数的匹配原则

我们看一个案例熟悉模板参数的匹配原则

案例:当模板函数和加法函数同时存在

//int的加法函数
int Add(int left, int right)
{
  cout << "调用int加法函数" << endl;
  return left + right;
}

//模板加法函数
template<class T1, class T2>
T1 Add(T1 left, T2 right)
{
  cout << "调用模板加法函数" << endl;
  return left + right;
}

void TestAdd1()
{
  int ret=Add(1, 2);    
  cout << ret << endl;//类型都是int 此时会调用int加法函数
  ret=Add(1, 2.0);
  cout << ret << endl;//此时不全是int,会调用模板加法函数
  ret=Add<int>(1, 2.0);
  cout << ret << endl;//实例化后也会调用模板加法函数   
}
int main()
{
  TestAdd1();
}

对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而 不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板

类模板的使用

先看一个我们完成栈的部分编程代码,此时这个程序只是支持int类型的数据

//栈的实现
class Stack
{
public:
	Stack(int capacity = 0)
	{
		_a = new int[capacity];
		_capacity = capacity;
		_top = 0;
	}
	~Stack()
	{
		delete[]_a;
		_capacity = 0;
		_top = 0;
	}
	void Push(int x)
	{}

private:
	int* _a;
	int _top;
	int _capacity;

};

int main()
{
	Stack st1;
	st1.Push(1);
	Stack st2;
	st2.Push(2.2);
}

我们如果想插入double数据,在数据定义、申请内存、插入数据时都需要修改int,耽误时间,这时我们就可以使用类模板

类模板使用格式:

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

我们在用类模板写一下上面的栈实现

template<class T>
class Stack//类模板名
{
public:
	Stack(int capacity = 0)
	{
		_a = new T[capacity];//new capacity个 T类型
		_capacity = capacity;
		_top = 0;
	}
	~Stack()
	{
		delete[]_a;
		_capacity = 0;
		_top = 0;
	}
	void Push(T x)
	{}

private:
	T* _a;
	int _top;
	int _capacity;

};
int main()
{
	Stack<int> st1;//不能自动推导
	st1.Push(1);//调用就可以传是int的

	Stack<double> st2;
	st2.Push(2.2); //调用就是传double的
}

使用类模板实现栈我们只需要在mian函数中将实例化的(double或int)类型放在<>中即可,省去我们去函数中一个个修改的时间

类模板的实例化

//类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的
//类型放在<>中即可
Vector类名,Vector<int>才是类型
Vector<int> s1;
Vector<double> s2;

注意:类模板名字不是真正的类,而实例化的结果才是真正的类

模板的分离编译

一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有 目标文件链接起来形成单一的可执行文件的过程称为分离编译模式

注意事项:

当我们把上面加法模板函数声明和定义分别放在.h和.cpp中时运行

// Add.h
template<class T>
T Add(const T& left, const T& right);
// Add.cpp
template<class T>
T Add(const T& left, const T& right)
{
  return left + right;
}
 //main.cpp
int main()
{
  Add(1, 2);
  Add(1.0, 2.0);
  return 0;
}

当我们把模板函数声明和定义写在两个文件中,会出现以上链接错误报错

解决方法:我们将声明和定义放到一个文件 "xxx.hpp" 里面,就会运行正常

// Add.hpp
template<class T>
T Add(const T& left, const T& right);
template<class T>
T Add(const T& left, const T& right)
{
  return left + right;
}
 
//main.cpp
int main()
{
  Add(1, 2);
  Add(1.0, 2.0);
  return 0;
}

所以,模板函数不支持声明和定义写在两个文件中,会出现链接错误,如果要分离编译的话,可以将声明和定义都放在头文件中,然后修改文件名后缀为.hpp

想了解更多分离编译内容的话,可以去这个链接http://blog.csdn.net/pongba/article/details/19130

希望这篇文章大家有所收获,我们下篇见


原文链接:https://blog.csdn.net/qq_72486849/article/details/125835436

栏目分类
最近更新