C++模板

函数模板

函数模板,是可以创建一个通用的函数,可以支持多种形参。

用关键字 template 来定义,

在函数模板中,数据的值和类型都被参数化了,发生函数调用时编译器会根据传入的实参来推演形参的值和类型。

template <class 类型名1,class 类型名2…>
返回值 函数名(形参表列) 模板参数表
{
    // 函数体
}

第一行的template<class 类型名1,class 类型名2…>是一句声明语句

template 是定义模板函数的关键字

尖括号里可以有多个类型

用class(或者typename来定义)。

然后后面跟定义的函数模板,切记中间不可以加其他的语句,不然会报错!

#include <iostream>
#include <stdlib.h>
using namespace std;

template <typename T>
void print(T);

int main()
{
	print(55);
	print('Y');
	print("Hello");
	system("pause");
	return 0;
}

template <typename T>
void print(T x)
{
	cout
		<< x
		<< endl;
}

函数模板也可以提前声明,不过声明时需要带上模板头,并且模板头和函数定义(声明)是一个不可分割的整体,它们可以换行,但中间不能有分号。

类模板

的定义格式:

template<typename 类型参数1 , typename 类型参数2 , …> class 类名{
    //TODO:
};

例:

template<class T1,class T2>
class Student 
{
	// 成员变量  成员函数
};

在类外定义成员函数时仍然需要带上模板头

成员函数定义模板格式:

template<typename 类型参数1 , typename 类型参数2 , …>
返回值类型 类名<类型参数1 , 类型参数2, ...>::函数名(形参列表){
    //TODO:
}

例:

template<class T1, class T2>
void Student<T1, T2>::say(){
};

构造函数 以及 析构函数 的模板 ,两者类似,析构函数只需要 多添加一个 ~ 符,且没有参数

template<typename 类型参数1 , typename 类型参数2 , …>
类名<类型参数1 , 类型参数2, ...>::类名(形参列表) : 变量初始化{
    //TODO:
}

例:

// 构造函数
template<class T1, class T2>
Student<T1, T2>::Student(T1 a, T2 b) :m_a(a), m_b(b) {};

// 析构函数
template<class T1, class T2>
Student<T1, T2>::~Student() {
};

指针对象 注意事项:

  • 赋值号两边的数据类型必须一致

  • 赋值号右边需要指明数据类型

格式:
类名<类型参数1,类型参数2 ····> 对象名 = new 类名<类型参数1,类型参数2 ····>(初始化参数1,参数2 ·····);

例:
Student<string, string> *Man = new Student<string, string>("浑元形意太极门掌门人", "马保国");
#include <iostream>
#include <string>

using namespace std;
template<class T1,class T2>
class Student 
{
private:
	T1 m_a;
	T2 m_b;
public:
	Student(T1 a, T2 b);
	void say();
	~Student();
};
// 构造函数
template<class T1, class T2>
Student<T1, T2>::Student(T1 a, T2 b) :m_a(a), m_b(b) {};
// 成员函数
template<class T1, class T2>
void Student<T1, T2>::say(){
	cout << this->m_a << this->m_b << endl;
};
// 析构函数
template<class T1, class T2>
Student<T1, T2>::~Student() {
	cout << this->m_a << this->m_b << endl;
};

int main()
{
	Student<string,string> Sir("浑元形意太极门掌门人","马保国");
	Sir.say();
	Student<int,char> c(2,'B');
	c.say();
	Student<string, string> *Man = new Student<string, string>("练习两年半的偶像练习生", "坤坤");
	Man->say();
	delete Man;

	return 0;
}

结果:
浑元形意太极门掌门人马保国
2B
练习两年半的偶像练习生坤坤
练习两年半的偶像练习生坤坤
2B
浑元形意太极门掌门人马保国

类模板的静态成员

静态模板成员变量
static 数据类型 变量名;

初始化格式:
template<typename 类型参数1 , typename 类型参数2 , …>
数据类型 类名<类型参数1 , 类型参数2, ...>::变量名 = 数据;

注意: 数据类型定义与初始化要一致

不使用模板定义静态变量:

static T1 count1;

// 模板静态成员变量初始化	
template<class T1, class T2>
T1 Student<T1, T2>::count1 = 0;

使用固定类型定义静态变量:

static int count2;

// 模板静态成员变量初始化
template<class T1, class T2>
int Student<T1, T2>::count2 = 0;

注意:

  • 静态成员变量,可以在类的内部使用,可以在外部进行操作

  • 静态成员变量使用模板定义时,当传入的模板类型不是可以操作的类型就会报错

原本希望传入的是整型,然后进行++操作

结果传入了string类型,由于不匹配所以会出现以下错误提示:

错误	C2676	二进制“++”:“T1”不定义该运算符或到预定义运算符可接收的类型的转换
#include <iostream>
#include <string>

using namespace std;

template<class T1, class T2>
class Student
{
public:
	static T1 count;    // 计数
private:
	T1 m_a;
	T2 m_b;
public:
	Student(T1 a, T2 b);
	void say();
};

// 构造函数
template<class T1, class T2>
Student<T1, T2>::Student(T1 a, T2 b) :m_a(a), m_b(b) {
	//count++; //传入的类型T1 不为整数可操作类型,就会报错, C2676
};

// 成员函数
template<class T1, class T2>
void Student<T1, T2>::say() {
	cout  << ". " << this->m_a << this->m_b << endl;
	
	// 静态成员变量初始化之后,在类中使用时,会引发异常  此位置的异常 : return (_CSTD strlen(_First));
	//cout << this->count << endl;
};

template<class T1, class T2>
T1 Student<T1, T2>::count = 0;// 仅初始化,不在类中使用,就不会出现问题,但也就好无意义了

int main()
{
	Student<string, string> Sir("浑元形意太极门掌门人", "马保国");
	// 因为传入了string类型,count可以手动赋值来计数,但无法通过,会引发异常, 此位置的异常 : return (_CSTD strlen(_First));
	// Sir.count = "1";
	Sir.say();
	return 0;
}

引起一系列问题的原因可能为:

1. 设置的类型初始化问题
    不能匹配确定是否合理初始化
    
2. 设置类型与想要操作的类型不匹配
    希望使用 ++等一些列运算符 操作的 整数或浮点数类型
    时间类型为 string

传入类型合理类型,修改上例的相关区域:

// 构造函数
template<class T1, class T2>
Student<T1, T2>::Student(T1 a, T2 b) :m_a(a), m_b(b) {
	count++; 
};
// 成员函数
template<class T1, class T2>
void Student<T1, T2>::say() {
	cout << ". " << this->m_a << this->m_b << endl;
	cout << this->count << endl;
};

// 初始化
template<class T1, class T2>
T1 Student<T1, T2>::count = 0;


// 创建对象
Student<int, string> Sir(2,"B" );
Sir.count++;
Sir.say();


结果:
. 2B
2

因此解决此类问题,就可以为static类型变量,创建 特定的构造函数 以及 模板

#include <iostream>
#include <string>

using namespace std;

// T3专门用来给静态变量传递模板数据类型
template<class T1, class T2,class T3>
class Student
{
public:
	static T3 count;   // 计数
private:
	T1 m_a;
	T2 m_b;
public:
	Student(T1 a, T2 b);
	void say();
};

// 构造函数,可以设置空的构造函数,之后通过接口传值,此处就省略
template<class T1, class T2,class T3>
Student<T1, T2, T3>::Student(T1 a, T2 b) :m_a(a), m_b(b) {
	count++;
};
// 成员函数
template<class T1, class T2, class T3>
void Student<T1, T2, T3>::say() {
	cout << this->count  << ". " << this->m_a << this->m_b << endl;
};

// 静态成员变量的初始化
template<class T1, class T2, class T3>
T3 Student<T1, T2, T3>::count = 0;

int main()
{
	Student<string, string,int> Sir("浑元形意太极门掌门人", "马保国");
	Sir.say();

	Student<string, string,int> *Man = new Student<string, string,int>("练习两年半的偶像练习生", "坤坤");
	Man->say();
	delete Man;

	Student<int, char,int> c(2, 'B');
	c.say();	
	Student<int, char,int> l(3, 'B');
	l.say();	
	Student<int, char,int> s(6, 'B');
	s.say();
	return 0;
}

结果:
1-1. 浑元形意太极门掌门人马保国
2-2. 练习两年半的偶像练习生坤坤
1-1. 2B
2-2. 3B
3-3. 6B
  • 通过静态变量可以知道,每次类的模板不同,就会产生新的类,且不互通

上例产生了两个类的格式

  • Student<int, char,int>
  • Student<string, string,int>

类模板与友元函数

// 模板类
template<typename T>
class Student
{
private:
	T m_x;
	T m_y;
public:
// 构造函数
	Student(T x, T y) :m_x(x), m_y(y) {};
// 友元函数
	friend T sum(Student<T> s);
};

// 函数模板
template<typename T>
T sum(Student<T> s)
{
	return s.m_x + s.m_y;
}

严重性	代码	说明	项目	文件	行	禁止显示状态
错误	LNK2019	无法解析的外部符号 "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl sum(class Student<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >)" (?sum@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$Student@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@@@Z),该符号在函数 "void __cdecl Study(void)" (?Study@@YAXXZ) 中被引用	学习	E:\C++\学习\学习\Study.obj	1	

类模板 不能 引入函数模板,虽然程序没有错误,但是在编译时就会出错

友元函数 需要指明 具体的参数类型 ,可以通过 重载 去定义多个 友元函数

注意: 友元函数 无法直接 访问类模板的参数,需要传递 类的对象对象指针

#include <iostream>
#include <string>

using namespace std;

// 模板类
template<typename T>
class Student
{
private:
	T m_x;
	T m_y;
public:
// 构造函数
	Student(T x, T y) :m_x(x), m_y(y) {};
// 友元函数的声明
	friend string sum(Student<string> s);
	friend int sum(Student<int> s);
};

// 友元函数的定义
string sum(Student<string> s)
{
	return s.m_x + s.m_y;
}
int sum(Student<int> s)
{
	return s.m_x + s.m_y;
}

int main()
{
	Student<string> Sir("浑圆形意太极门掌门人", "马保国");
	cout << sum(Sir) << endl;
	
	Student<int> Age(6,9);
	cout << sum(Age) << endl;
	return 0;
}
类模板与 友元 类的成员函数

friend 表示 出现 friend 的类

member 表示 成员函数的类

定义的一般顺序:

第一步:
class friend;

第二步:
template <class T1, ···· >
class member{
    // 成员变量 , 成员函数
    // 需要 友元的成员函数 声明
}

第三步:
class friend{
    // 成员变量 , 成员函数
    // friend member类的 友元成员函数
}

第四步:
member类的 友元成员函数 的实现

例子:

#include <iostream>
#include <string>

using namespace std;

template<typename T>
class Student;		// 同样很有必要的声明


// 第一个类, member类
class Introduct
{
public:
	// 成员函数
	string sum(Student<string>* s);	// 对象指针
	int sum(Student<int> s);		// 对象
};

// 第二个类,友元类
template<typename T>
class Student
{
private:
	T m_x;
	T m_y;
public:
// 构造函数
	Student(T x, T y) :m_x(x), m_y(y) {};
// 友元函数的声明
	friend string Introduct::sum(Student<string>* s);
	friend int Introduct::sum(Student<int> s);
};


// 友元函数的定义
string Introduct::sum(Student<string>* s)
{
	return s->m_x + s->m_y;
}
int Introduct::sum(Student<int> s)
{
	return s.m_x + s.m_y;
}


int main()
{
	Introduct Mk;

	Student<string> Sir("浑圆形意太极门掌门人", "马保国");
	cout << Mk.sum(&Sir) << endl;

	Student<int> Age(6,9);
	cout << Mk.sum(Age) << endl;
	return 0;
}

结果:
浑圆形意太极门掌门人马保国
15

通过 重载 来扩展, member类的使用

同样需要传递 实例对象 或 实例对象指针,访问类的成员

类模板与 友元 模板类的成员函数

两个类模板之间

  1. 提前声明 member类
  2. 定义 friend类, 引入友元的成员函数
  3. 定义 member类
  4. 定义 模板成员函数
#include <iostream>
#include <string>

using namespace std;

template<typename T>
class Introduct;        // 有必要的提前定义

// 第一个类, 友元类
template<typename T>
class Student
{
private:
	T m_x;
	T m_y;
public:
	Student(T x, T y) :m_x(x), m_y(y) {};
	// 友元类
	friend T Introduct<T>::sum(Student<T> s);
};


// 第二个类, member类
template<typename S>
class Introduct
{
public:
	// 成员函数
	S sum(Student<S> s);
};

// 模板成员函数
template<typename S>
S Introduct<S>::sum(Student<S> s)
{
	return s.m_x + s.m_y;
}

int main()
{
    Introduct<string> Mk;
	Student<string> Sir("浑圆形意太极门掌门人", "马保国");
	cout << Mk.sum(Sir) << endl;

	Introduct<int> M;
	Student<int> Age(6, 9);
	cout << M.sum(Age) << endl;
	return 0;
}

注意:
在使用模板时,前后要对应

类模板 与 友元 类

优先定义 友元类

#include <iostream>
#include <string>

using namespace std;

// 第一个类, 友元类
template<typename T>
class Student
{
private:
	T m_x;
	T m_y;
public:
	Student(T x, T y) :m_x(x), m_y(y) {};
	// 友元类
	friend class Introduct;
};


// 第二个类, member类
class Introduct
{
public:
	// 成员函数
	string sum(Student<string>* s);	// 对象指针
	int sum(Student<int> s);		// 对象
};

// Introduct成员函数
string Introduct::sum(Student<string>* s)
{
	return s->m_x + s->m_y;
}
int Introduct::sum(Student<int> s)
{
	return s.m_x + s.m_y;
}

int main()
{
	
	Introduct Mk;

	Student<string> Sir("浑圆形意太极门掌门人", "马保国");
	cout << Mk.sum(&Sir) << endl;

	Student<int> Age(6,9);
	cout << Mk.sum(Age) << endl;
	return 0;
}

结果:
浑圆形意太极门掌门人马保国
15

类模板 与 友元类类模板与 类的友元成员函数 类似,不同点就是前者无需过多声明 成员函数,后者需要依次 声明 需要用到的 成员函数

类模板 与 友元 类模板

两个 类模板

  1. 提前声明 member类
  2. 定义 friend类 , 引入 友元类
  3. 定义 member类
  4. 定义 模板成员函数
#include <iostream>
#include <string>

using namespace std;

template<typename T>
class Introduct;        // 很有必要的声明

// 第一个类, 友元类
template<typename T>
class Student
{
private:
	T m_x;
	T m_y;
public:
	Student(T x, T y) :m_x(x), m_y(y) {};
	// 友元类
	friend class Introduct<T>;
};


// 第二个类, member类
template<typename S>
class Introduct
{
public:
	// 成员函数
	S sum(Student<S> s);
};

template<typename S>
S Introduct<S>::sum(Student<S> s)
{
	return s.m_x + s.m_y;
}


int main()
{
	
	Introduct<string> Mk;
	Student<string> Sir("浑圆形意太极门掌门人", "马保国");
	cout << Mk.sum(Sir) << endl;

	Introduct<int> M;
	Student<int> Age(6, 9);
	cout << M.sum(Age) << endl;
	
	return 0;
}

结果:
浑圆形意太极门掌门人马保国
15

类模板与非类型参数

在定义模板的时候,可以引入参数

不能使用 结构体string 等定义非类型参数

template <class T, 数据类型 参数名, ······ >

例如:
template <class T1 , int count , class T2>
class Student
{
}

在传递参数时,需要传递相对应的数据类型的数据
Student<int, 2 ,char> Sir;

例如:

#include <iostream>
#include <string>

using namespace std;

// 模板类
template<typename T, int count>
class Student
{
private:
	T m_n;
public:
	// 构造函数
	Student(T n) :m_n(n){};
	T say()
	{
		m_n += count;
		return m_n;
	}
};

// 

void Study()
{
	Student<int,2> Sir(1);
	cout << Sir.say() << endl;
	cout << Sir.say() << endl;
	cout << Sir.say() << endl;
	cout << Sir.say() << endl;
}

结果:
3
5
7
9

类模板的继承

基类模板 与 子类
#include <iostream>
#include <string>

using namespace std;

// 基类
template <class T>
class People
{
public:
	T m_num;
public:
	// 构造函数
	People(T num) : m_num(num) {};
	// 成员变量
	void show()
	{
		cout << "num = " << this->m_num << endl;
	}
};

// 子类
class Student : public People<int> {
private:
	int m_code;
public:
	// 构造函数
	Student(int code, int num) :m_code(code), People<int>(num) {};
	// 成员变量
	void show()
	{
		cout << "code = " << this->m_code << ";"
			 << "num = " << this->m_num << endl;
	}
};


int main()
{
	People<int> sir_1(10);
	sir_1.show();

	Student sir_2(996, 777);
	sir_2.show();
	
	return 0;
}

结果:
num = 10
code = 996;num = 777
基类模板 与 子类模板
#include <iostream>
#include <string>

using namespace std;

// 基类
template <class T>
class People
{
public:
	T m_num;
public:
	// 构造函数
	People(T num) : m_num(num) {};
	// 成员变量
	void show()
	{
		cout << "num = " << this->m_num << endl;
	}
};

// 派生类
template <class S>
class Student : public People<S> {
private:
	S m_code;
public:
	// 构造函数
	Student(S code, S num) :m_code(code), People<S>(num) {};
	// 成员变量
	void show()
	{
		cout << "code = " << this->m_code << ";"
			 << "num = " << this->m_num << endl;
	}
};


int main()
{
	People<int> sir_1(10);
	sir_1.show();

	Student<int> sir_2(996, 777);
	sir_2.show();
	
	return 0;
}

结果:
num = 10
code = 996;num = 777

注意:构造函数,基类要指明类型,继承是也要指明类型,且前后照应

模板实参推断

从函数实参来确定模板实参的过程被称为模板实参推断(template argument deduction)

使用函数模板显示实参,可以覆盖实参推断机制

#include <iostream>
#include <string>

using namespace std;

template<typename T1, typename T2>
int max(T1 a, T2 b)
{
	cout
		<< "T1 = " << sizeof(T1) << endl
		<< "T2 = " << sizeof(T2) << endl;
		
	if (a > b) return a;
	else if (a == b) return 0;
	else return b;
}

int main()
{
	short sh = 6;
	
	// 系统推断
	cout << max(sh, 3) << endl; 
	// 显式定义
	cout << max<int>(sh, 3) << endl; 
	cout << max<int,int>(sh, 3) << endl;
	return 0;
}

结果:
T1 = 4
T2 = 2
6
T1 = 4
T2 = 2
6
T1 = 4
T2 = 4
6

模板实例化

模板实例化是生成采用特定模板参数组合的具体类或函数(实例)

编译器生成一个采用 Array 的类,另外生成一个采用 Array 的类。通过用模板参数替换模板类定义中的模板参数,可以定义这些新的类。

隐式实例化

隐式实例化:通过编译器自己推测判断要实例化的类型。

编译器会根据实参推断类型

显式实例化

模板显式实例化(extern template)可以用来确保模板只被编译器实例化一次

  • 使用模板生成的编译单元不会重复实例化,会加快编译速度,并减小编译单元的尺寸

要显式实例化模板函数,在 template 关键字后接函数的声明(不是定义),且函数标识符后接模板参数。

template 返回值类型 函数名 <数据类型>

例如:
template<typename T1, typename T2>
T1 max(T1 a, T2 b)
{
	if (a > b) return a;
	else if (a == b) return 0;
	else return b;
}

template int max<int,int>(int a, int b);  //显式实例化,只需声明

模板类的显式实例化

template class 类名<数据类型>

例如:
template<typename T>
class Student
{
private:
	T m_x;
	T m_y;
public:
	Student(T x,T y) :m_x(x), m_y(y) {};
	void prin()
	{
		cout << m_x << m_y << endl;
	}
};

// 显式实例化 类模板
template class Student<char>;
template class Student<string>;

模板类静态数据成员的显式实例

template 返回值类型 类名<数据类型>::函数名(参数列表);

例如:
template<typename T>
class Student
{
private:
	T m_x;
	T m_y;
public:
	Student(T x,T y) :m_x(x), m_y(y) {};
	T sum(T a);
};

template<typename T>
T Student<T>::sum(T a)
{
	return this->m_x + this->m_y + a;
}

// 显式实例化 类模板
template string Student<string>::sum(string a);
template int Student<int>::sum(int a);

注意:类型的规范,使用相同模板在带入数据类型时,也要同步,不要随心所欲,想使用不同类型就分开定义,一句话:早知现在何必当初

模板显式具体化

让模板能够针对某种具体的类型使用不同的算法(函数体或类体不同),称为模板的显示具体化(Explicit Specialization)

利用对函数模板的显式具体化,对于 数组结构体 数据类型进行操作

C++98标准 : 原型和定义以 template<> 开头,并通过名称指出类型。函数调用优先级是 非模板函数 > 具体化模板函数 > 常规模板函数

函数模板类模板 的基础上,新添加一个专门针对 特定类型 的、实现方式不同具体化 函数或类

模板函数显式具体化

template<> 
数据类型 函数名<复杂数据类型>(参数列表){
    // 函数体
}

例如:
template <class T>
T stripling(T a, T b)
{
	return a < b ? a : b;
};

// 只有定力了原型模板,才能定义如下的显式具体化,原型模板如上 template <class T> T stripling(T a, T b)
// People 为定义的结构体类型
template <>
People stripling<People>(People a, People b)
{
	return a.age < b.age ? a : b;
};
#include <iostream>
#include <string>

using namespace std;

typedef struct{
	string name;
	int age;
}People;

// 输出 较大值
template <class T>
T stripling(T a, T b)
{
	return a < b ? a : b;
};

// 需要有原型,如上 template <class T> T stripling(T a, T b)
template <>
People stripling<People>(People a, People b)
{
	return a.age < b.age ? a : b;
};

void prin(People sir)
{
	cout << sir.name << "," << sir.age << "岁,不讲武德" << endl;
}

int main()
{
	People young1 = { "八十公斤的年轻人",35 };
	People young2 = { "九十公斤的年轻人",30 };
	People leader = { "马保国",69 };
	prin(stripling(young1,leader));
	prin(stripling(young2,leader));
	return 0;
}

模板类的显式具体化

在类模板的具体化中,成员方法的实例化是不能带模板头template<>的。

template<>
class 类名<复杂数据类型>
{
    // 成员变量
    // 成员函数
}

例如:
template <class T1, class T2>
class People {
    // 成员变量 成员函数
}

// msg 为 结构体
template<>
class People<msg , msg>
{
    // 成员变量 成员函数
}
#include <iostream>
#include <string>

using namespace std;

template <class T1, class T2>
class People
{
private:
// 成员变量
	T1 m_x;
	T2 m_y;
public:
// 构造函数
	People(T1 x, T2 y) :m_x(x), m_y(y) {};
// 成员函数
	void show();
};

template <class T1, class T2>
void People<T1,T2>::show()
{
	cout << m_x << "," << m_y << endl;
}

// 类模板显式具体化(针对 结构体 的显式具体化)
typedef struct
{
	string name;
	int age;
} msg ;

template<>
class People<msg , msg>
{
private:
// 成员变量
	msg m_leader;
	msg m_young;
public:
// 构造函数
	People(msg leader,msg young) :m_leader(leader), m_young(young){};
// 成员函数
	void show();
};

// 注意!这里不能带模板头template<>
void People<msg, msg>::show()
{
// 判断 年轻人 与 掌门人 年龄
	msg stripling = this->m_leader.age > this->m_young.age ? m_leader : m_young;
// 输出年龄大的老同志
	cout << stripling.name << ":" << stripling.age << endl << "马家功夫名不虚传" << endl;
}


int main()
{
	People<string, string> Sir("浑圆形意太极门掌门人","马保国");
	Sir.show();

	msg young = { "八十公斤的年轻人",35 };
	msg leader = { "马保国",69 };
	People<msg, msg> Man(leader,young);
	Man.show();
	return 0;
}

结果:
浑圆形意太极门掌门人,马保国
马保国:69
马家功夫名不虚传
部分显式具体化

部分显式具体化只能用于类模板,不能用于函数模板

template<class 模板类型>
class 类名<复杂数据类型 , 模板类型>         // 模板类型 要 对应,位置不固定,根据情况而定
{
    // 成员变量
    // 成员函数
}

例如:
template <class T1, class T2>
class People {
    // 成员变量 成员函数
}

// msg 为 结构体
template<class T>
class People<msg , T>
{
    // 成员变量 成员函数
}
#include <iostream>
#include <string>

using namespace std;

template <class T1, class T2>
class People
{
private:
// 成员变量
	T1 m_x;
	T2 m_y;
public:
// 构造函数
	People(T1 x, T2 y) :m_x(x), m_y(y) {};
// 成员函数
	void show();
};

template <class T1, class T2>
void People<T1,T2>::show()
{
// 输出数据
	cout << m_x << "," << m_y << endl;
}

// 部分显式具体化(针对 结构体 的部分显式具体化)
typedef struct
{
	string name;
	int age;
}msg;

template<typename T>
class People<msg , T>
{
private:
// 成员变量
	msg m_leader;
	T m_sure_age;
public:
// 构造函数
	People(msg leader,T sure_age) :m_leader(leader), m_sure_age(sure_age){};
// 成员函数
	void show();
};

// 注意!需要带模板头
template<typename T>
void People<msg, T>::show()
{
// 修改数据,并输出
	this->m_leader.age = this->m_sure_age;
	cout << this->m_leader.name << ":" << this->m_leader.age << endl << "马家功夫名不虚传" << endl;
}


int main()
{
	People<string, string> Sir("浑圆形意太极门掌门人","马保国");
	Sir.show();

	msg leader = { "马保国",35 };
	People<msg, int> Man(leader,69);
	Man.show();
	
	return 0;
}

结果:
浑圆形意太极门掌门人,马保国
马保国:69
马家功夫名不虚传

模板 用于多文件编程

在将函数用于多文件编程时,我们通常是将 函数定义 放在 源文件(.cpp 文件) 中,将 函数声明 放在 头文件(.h文件)中,使用函数时 引入(#include 命令) 对应的 头文件 即可

编译是针对单个源文件的,只要有函数声明,编译器就能知道函数调用是否正确;而将函数调用和函数定义对应起来的过程,可以延迟到链接时期。正是有了连接器的存在,函数声明和函数定义的分离才得以实现。

模板并不是真正的函数或类,它仅仅是用来生成函数或类的一张 “图纸”,在这个生成过程中有三点需要明确:

  • 模板的实例化是按需进行的,用到哪个类型就生成针对哪个类型的函数或类,不会提前生成过多的代码

  • 模板的实例化是由编译器完成的,而不是由链接器完成的

  • 在实例化过程中需要知道模板的所有细节,包含声明和定义(只有一个声明是不够的,可能会在链接阶段才发现错误)

头文件:s.h ,存放类模板的定义

#ifndef s
#define s

template<typename T>
class Student
{
private:
	T m_x;
	T m_y;
public:
	Student(T x, T y) :m_x(x), m_y(y) {};
	T sum();
};

#endif

cpp文件,定义类的成员函数

#include <iostream>
#include <string>
#include "s.h"

using namespace std;

template<typename T>
T Student<T>::sum()
{
	return this->m_x + this->m_y ;
}

main函数调用

#include "s.h"
#include <iostream>
#include <stdlib.h>
using namespace std;
int main()
{
	Student<int> Sir(1, 3);
	cout << Sir.sum() << endl;

	system("pause");
	return 0;
}

直接就报错

严重性	代码	说明	项目	文件	行	禁止显示状态
错误	LNK2019	无法解析的外部符号 "public: int __thiscall Student<int>::sum(void)" (?sum@?$Student@H@@QAEHXZ),该符号在函数 _main 中被引用	学习	E:\C++\学习\学习\学习.obj	1	

错误	LNK1120	1 个无法解析的外部命令	学习	E:\C++\学习\Debug\学习.exe	1	

但如果将 类的成员函数 定义放到 头文件:s.h ,这些错误就没有了

不能将模板的声明和定义分散到多个文件中的根本原因是:模板的实例化是由编译器完成的,而不是由链接器完成的,这可能会导致在链接期间找不到对应的模板实例。

参考:

  1. 将 C++ 模板应用于多文件编程

由于参考内容过多,在这里就不一一列举,如若有问题,请联系我,会及时修改,添加!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值