函数模板
函数模板,是可以创建一个通用的函数,可以支持多种形参。
用关键字 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类的使用
同样需要传递 实例对象 或 实例对象指针,访问类的成员
类模板与 友元 模板类的成员函数
两个类模板之间
- 提前声明 member类
- 定义 friend类, 引入友元的成员函数
- 定义 member类
- 定义 模板成员函数
#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
类模板 与 友元类
和 类模板与 类的友元成员函数
类似,不同点就是前者无需过多声明 成员函数,后者需要依次 声明 需要用到的 成员函数
类模板 与 友元 类模板
两个 类模板
- 提前声明 member类
- 定义 friend类 , 引入 友元类
- 定义 member类
- 定义 模板成员函数
#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
,这些错误就没有了
不能将模板的声明和定义分散到多个文件中的根本原因是:模板的实例化是由编译器完成的,而不是由链接器完成的,这可能会导致在链接期间找不到对应的模板实例。
参考:
由于参考内容过多,在这里就不一一列举,如若有问题,请联系我,会及时修改,添加!!!