添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
帅气的弓箭  ·  烎Excel ...·  2 年前    · 
曾经爱过的西装  ·  ComponentScans ...·  2 年前    · 

模板是泛型编程的基础,泛型编程即以一种 独立于任何特定类型 的方式编写代码。

模板是创建泛型或函数的蓝图或公式。容器、迭代器、算法等,都是泛型编程的例子。

在C++中,模板分为函数模板和类模板两种。

1. 函数模板:

函数模板不是一个实在的函数, 编译器不能为其生成可执行代码。

定义函数模板后只是一个对参数功能框架的描述,当它具体执行时,将根据传递的实际参数决定其功能。

编译器由模板自动生成函数时,会用具体的类型名对模板中所有的类型参数进行替换,其他部分则原封不动的保留。
同一个类型参数只能替换为同一种类型,编译器在编译到调用函数模板的语句时,会根据实参的类型推断该如何替换模板中的类型参数。

template <typename 类型参数1, typename 类型参数2, ...>
返回值类型  函数名(形参列表) 

其中,“类型参数” 是函数所使用的数据类型的 占位符 名称,“返回值类型”、“形参列表”、“函数体” 中都可以使用类型参数占位符。

编译器由模板自动生成函数的过程叫“模板的实例化”。 由模板实例化而得到的函数称为模板函数。

在某些编译器中,模板只有在被实例化时,编译器才会检查其语法正确性。 如果程序中写了一个模板却没有用到,那么编译器不会报告这个模板中的语法错误。

编译器在对模板进行实例化时,并非只能通过调用语句的实参来实例化模板中的类型参数,模板调用语句可以明确指明要把类型实例化为哪种类型。
方法是:

模板名<实际类型参数1, 实际类型参数2, ...>
//定义函数模板:一个用于比较两数大小的函数,与类型无关:
template <typename T>
inline T const& Max(T const& a, T const& b) 
	return a < b ? b : a;
//函数模板的实例化:
int main() {
	int a = 4, b = 5;
    int c = Max(a, b);
    cout << c << endl;
	double x = 8.1, double y = 1.8;
	double z = Max(x, y);
	cout << z << endl;
	//模板调用语句可以明确的指明要把类型参数 实例化为哪种类型:
	float f = Max<float>(7.7, 6,6);
	cout << f << endl;

函数模板的实例化有两种方式:

  1. 通过模板调用语句的实参来实例化模板中的类型参数;
  2. 在模板调用语句中明确指明要把类型参数实例化为哪种类型。

1.1 IM项目中对函数模板的实际应用:

//定义函数模板:
//函数的作用是向*server_list链表中的所有服务器发起connect主动连接,并将连接信息保存到server_list中,
//每个连接对应一个T类型的本地实例,使用模板则可以在多个不同类型的server上复用:
template <typename T> 
void serv_init(serv_info_t* server_list, uint32_t server_count)
	for(uint32_t i = 0; i < server_count; i++) {
		T* pConn = new T();
		pConn->Connect(serv_list[i].server_ip.c_str(), server_list[i]);
		server_list[i].serv_conn = pConn;
		server_list[i].idle_cnt = 0;
		server_list[i].reconnect_cnt = MIN_RECONNECT_CNT / 2;
//实例化函数模板:
serv_init<CDBServConn>(g_db_server_list, g_db_server_count);
serv_init<CRouteServConn>(g_route_server_list, g_route_server_count);

2. 类模板:

当有两个及两个以上的类,如果其功能相同,仅仅是数据类型不同时,就可以采用类模板的方式实现代码的复用。

//类模板 声明:
template <typename 类型参数1, typename 类型参数2, ...>
//类模板 定义:
template <typename 类型参数1, typename 类型参数2, ...>
class 类名
public:
	类成员函数
private:
template <typename T>
class A 
public:
	A(T t) {
		this->t = t;
	T& getT() {
		return t;
private:
	T t;
int main() {
	A<int> a(100);
	a.getT();
    return 0;

2.1 继承中的类模板语法:

当子类从模板类继承的时候(父类是一个模板类),需要让编译器知道父类的具体数据类型是什么(本质上是为了让编译器知道所占用的内存空间大小)。

//定义一个模板类做为父类:
template <typename T>
class A 
public:
	A(T x) { t = x; }
	void out() { cout << t << endl; }
private:
	T t;
//一个继承模板类的子类:
//必须让编译器知道父类的具体数据类型:
class B : public A<int> {
public:
	B(int a, double x) : A<int>(a) { y = x; }
	void out() { A<int>::out(); cout << y << endl; }
private:
	double y;

2.2 类模板中的static关键字:

  1. 相同类型的类模板的static静态成员是所有类对象共享的,这与正常的类保持一致;
  2. 由于编译器对模板的编译方式使用的是“二次编译”,所以当类模板被初始化为多种实例类型时,也会出现多个对应的不同类型的static静态成员。

  当类模板中出现static修饰的静态成员的时候,我们只要按照正常理解就可以了。static的作用是将类的成员修饰成静态的,所谓的静态类成员就是指类的成员为类级别的,不需要实例化对象就可以使用, 而且的所有对象都共享同一个静态类成员,因为类静态成员是属于类而不是对象的。

  那么,类模板的实现机制是通过 “二次编译” 原理实现的。
  C++编译器并不是在第一次编译类模板的时候就把所有可能出现的类型都分别编译出对应的类(实际也不可能,C++中内置的数据类型太多了),而是在第一次编译的时候编译一部分,遇到泛型不会替换成具体的类型(此时编译器还不知道具体的类型),而是在第二次编译的时候再将泛型替换成具体的类型(此时编译器通过第一次编译已经知道程序中实例化了哪些具体类型)。

  由于类模板的“二次编译”原理再加上static关键字修饰的成员,当它们在一起的时候实际上一个类模板会被编译成多个具体类型的类,所以,不同类型的类模板对应的static成员也是不同的(归属于不同的类),但相同类型的类模板的static成员是共享的(归属于同一个类)。

2.3 IM项目中对类模板的实际应用:

//单例模式的模板类:
//类的构造函数封装为private,通过Instance()成员函数来获取实例:
template <typename T>
class Singleton {
public:
	static T& Instance() {
		if(Singleton::s_instance == 0) {
			Singleton::s_instance = CreateInstance();
			return *(Singleton::s_instance);
private:
	static T* s_instance;	
private:
	static T* CreateInstance() {
		return new T();
//实例化Singleton模板类
class TransferTaskManager : public Singleton<TransferTaskManager> 
public:
	~TransferTaskManager();
private:
	TransferTaskManager();

3. 关键字typename与class的区别:

在模板语法中关键字 class 与 typename 的作用完全一样。

在模板引入C++后,最初定义模板的方法是:
template<class T>......
在这里class关键字表明T是一个类型,后来为了避免class在这两个地方的使用可能给人带来混淆,所以引入了typename这个关键字,它的作用同class一样表明后面的符号为一个类型,这样在定义模板的时候就可以使用下面的方式:
template<typename T>......

参考内容:
https://debuger.blog.csdn.net/article/details/94622335

0. 概述:模板是泛型编程的基础,泛型编程即以一种 独立于任何特定类型 的方式编写代码。模板是创建泛型或函数的蓝图或公式。容器、迭代器、算法等,都是泛型编程的例子。在C++中,模板分为函数模板和类模板两种。1. 函数模板:函数模板不是一个实在的函数,编译器不能为其生成可执行代码。定义函数模板后只是一个对参数功能框架的描述,当它具体执行时,将根据传递的实际参数决定其功能。编译器由模板自动生成函数时,会用具体的类型名对模板中所有的类型参数进行替换,其他部分则原封不动的保留。同一个类型参数只能替换
本文针对C++函数模板类模板进行了较为详尽的实例解析,有助于帮助读者加深对C++函数模板类模板的理解。具体内容如下: 泛型编程(Generic Programming)是一种编程范式,通过将类型参数化来实现在同一份代码上操作多种数据类型,泛型是一般化并可重复使用的意思。泛型编程最初诞生于C++中,目的是为了实现C++的STL(标准模板库)。 模板(template)是泛型编程的基础,一个模板就是一个创建类或函数的蓝图或公式。例如,当使用一个vector这样的泛型类型或者find这样的泛型函数时,我们提供足够的信息,将蓝图转换为特定的类或函数。 一、函数模板 一个通用的函数模板(functi
c++ 11新增了很多新特性,方便了我们的使用。多参数模板就是其中一种特性。下面就用多参数模板来实现统一的类函数调用模板。 class MessageHandler; std::list<MessageHandler*> g_messageList; class MessageHandler { public: virtual ~MessageHandler() = default;...
eg:类模板而言 因为f()不依赖与模板参数T,所以它在第一阶段就会被解析,而它的基类X在第二阶段才会被解析,所以解析f()的时候只能看到全局作用域里的typedef double E (1)第一种方式调用了全局的g(), (2)第二种方式调用了基类的g(), (3)原因在于当编译器遇到第一种方式的g()时,发现它没有表现出要依赖于模板参数,所以认为它不依
模板C++支持参数化多态的工具,使用模板可以使用户为类或者函数声明一种一般模式,使得类中的某些数据成员或者成员函数的参数、返回值取得任意类型。 模板是一种对类型进行参数化的工具;   通常有两种形式:函数模板类模板;   函数模板针对仅参数类型不同的函数类模板针对仅数据成员和成员函数类型不同的类。 使用模板的目的就是能够让程序员编写与类型无关的代码。假定我们希望编写一个函
C语言:通过回调函数实现,使用者调用麻烦。 C++语言:函数重载,需要为多种类型实现一个第一版本,还会导致代码段增加。 C/C++语言:借助宏函数实现,类型检查不严格,频繁使用还会增加代码段。 由于以上原因C++之父在C++中实现了模板技术,既能技术多种类也能兼顾严格的类型检查,能让程序员编程专注思考业务逻辑而不用关系数据类型。 三、函数模板 1、函数模板的定义 template <typename
不解释,看实例 正常使用宏定义时,要获取符号名称,是使用#把名称字符串化,但这种方式在模板中不好使,需要更底层提供的运算符操作typeid。 template<typename T> class CTest { public: CTest() { sName = typeid(T).name(); std::string GetName() { return sName; private: std::string sName; int main() { 这个模板函数接受两个类型参数 `T` 和 `U`,并且返回 `a+b` 的结果,该结果的类型是根据 `a` 和 `b` 的类型推导出来的。例如,如果你调用 `add(3, 4.5)`,那么模板函数会返回 7.5,因为 `3` 是 `int` 类型,而 `4.5` 是 `double` 类型。 以下是一个完整的例子: ```c++ #include <iostream> template <typename T, typename U> auto add(T a, U b) -> decltype(a + b) { return a + b; int main() { std::cout << add(3, 4.5) << std::endl; // 输出 7.5 std::cout << add(3.2, 4.5) << std::endl; // 输出 7.7 std::cout << add(3, 'a') << std::endl; // 输出 100 (ASCII码值) return 0;