类模板语法

和函数模板的写法一样,在template的下一行紧跟一个类,就是类模板。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include<iostream>
#include<string.h>
using namespace std;
template<class typeAge,class typeName>
class Person{
public:
Person(typeAge age,typeName name){
this->_age = age;
this->_name = name;
}
typeAge _age;
typeName _name;
};
int main(){
Person<int, string> p(20,"Tom");
}

类模板特点

类模板没有自动类型推导的使用方式。

类模板在模板参数列表中可以有默认参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<iostream>
#include<string.h>
using namespace std;
template<class typeAge,class typeName>
class Person{
public:
Person(typeAge age,typeName name = string){
this->_age = age;
this->_name = name;
}
typeAge _age;
typeName _name;
};
int main(){
Person<int> p(20,"Tom");
}

例子中,name的默认参数是string。(默认参数的设置和函数默认参数的规则类似,默认参数必须要在非默认参数后面出现)

类模板中成员函数的创建时机

类模板中的成员函数在调用时才创建,一开始无法确定成员变量的数据类型,只有当调用了类模板后才能确定成员变量的数据类型,类模板中的成员函数在这个时机,才创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<iostream>
#include<string.h>
using namespace std;
class Person{
public:
void showPerson(){
printf("show");
}
};
template<class T>
class test{
public:
T obj;
void fun(){
obj.showPerson();
}
};
int main(){
test<Person> m;
//这个时候才创建fun()
m.fun();
return 0;
}
//输出show

例子中类模板class中的fun( )只有当接收了T的数据类型才能创建,若数据类型T不是Person,无法调用fun( )函数,还会报错。创建的时机是被调用后。

类模板对象做函数参数

一共有三种传入方式:

1、指定传入的类型: 直接显示对象的数据类型

事先定义好函数要传入的类型,如这里的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include<iostream>
#include<string.h>
using namespace std;

template<class T1,class T2>
class test{
public:
test(T1 age, T2 name){
this->age = age;
this->name = name;
}
void show(){
printf("show");
}
T1 age;
T2 name;
};
//指定传入类型
void showfunc(test<int, string> &p){
p.show();
}
int main(){
test<int, string> person(20,"Tom");
showfunc(person);
return 0;
}

2、参数模板化:将对象中的参数变为模板进行传递

1
2
3
4
5
//参数模板化
template<class T1, class T2>
void showfunc(test<T1, T2> &p){
p.show();
}

3、整个类模板化:将这个对象类型 模板化进行传递

1
2
3
4
5
//整个类模板化
template<class T>
void showfunc(T &p){
p.show();
}

第一种比较实用

类模板与继承

当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型

如果不指定,编译器无法给子类分配内存

1
2
3
4
5
6
7
8
template<class T>
class Base{
T m;
};
//指定父类里T的类型为int
class Son: public Base<int>{

};

如果想灵活指定出父类中T的类型,子类也需变为类模板‘

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include<iostream>
#include<string.h>
using namespace std;
template<class T>
class Base{
public:
T m;
};
//指定父类里T的类型为int
template<class T1, class T2>
class Son: public Base<T2>{
public:
T1 one;
};
int main(){
//这里的int经过子类T2传递到父类的T
//父类char m,子类中int one
Son<char, int> m;
}

类模板成员函数类外实现

假设有一个类Person

构造函数类外实现

1
2
3
4
5
6
7
8
9
10
11
class Person{
public:
T1 name;
T2 age;
};
//构造函数类外实现
template<class T1,class T2>
Person<T1,T2>::Person(T1 name,T2 age){
this->name = name;
this->age = age;
}

成员函数的类外实现(假设Person类存在)

1
2
3
4
5
template<class T1,class T2>
void Person<T1,T2>::showPerson(){

}

类模板分文件编写

讲.h和.cpp 中的内容写到一起,再将后缀名改为.hpp(约定俗成的)

将类模板和调用编写到一起,否则会导致链接不到类模板中的成员函数(因为该函数是在被调用时才生成)

解决方法:将声明和实现写到同一个.hpp文件中。

类模板和友元

全局函数 类内实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include<iostream>
#include<string.h>
using namespace std;
template<class T1, class T2>
class Person{
//类内直接用friend关键字声明
friend void printPerson(Person<T1,T2> &p){
cout<<p.name<<endl;
cout<<p.age;
}

public:
Person(T1 name, T2 age){
this->age = age;
this->name = name;
}
private:
T1 name;
T2 age;
};

int main(){
Person<string,int> p("Tom",20);
printPerson(p);

return 0;
}

全局函数 类外实现(比较复杂)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//提前让编译器知道Person类的存在
template<class T1, class T2>
class Person;
//类外实现全局函数(模板函数)
template<class T1, class T2
void printPerson(Person<T1,T2> &p){
cout<<p.name<<endl;
cout<<p.age;
}
template<class T1, class T2>
class Person{
//加空模板参数列表让编译器知道这是一个模板函数
friend void printPerson<>(Person<T1,T2> &p)
public:
Person(T1 name, T2 age){
this->age = age;
this->name = name;
}
private:
T1 name;
T2 age;
};

总结:建议写类内实现