c++模板--类模板
1 类模板的基本概念
类模板和函数模板的定义和使用类似,我们已经进行了介绍。有时,有两个或多个类,其功能是相同的,仅仅是数据类型不同。
类模板和函数模板区别:
- 类模板不可以使用自动类型推导,只能用显示指定类型
- 类模板中可以有默认参数
类模板中成员函数创建时机
- 类模板中的成员函数 并不是一开始创建的,而是在运行阶段确定出T的数据类型才去创建
//类模板用于实现类所需数据的类型参数化
template<class NameType, class AgeType>
class Person
{
public:
Person(NameType name, AgeType age)
{
this->mName = name;
this->mAge = age;
}
void showPerson()
{
cout << "name: " << this->mName << " age: " << this->mAge << endl;
}
public:
NameType mName;
AgeType mAge;
};
void test01()
{
//Person P1("德玛西亚",18); // 类模板不能进行类型自动推导
Person<string, int>P1("德玛西亚", 18);
P1.showPerson();
}
2 类模板做函数参数
- 指定传入类型
- 参数模板化
- 整个类模板化
通常使用方法一:指定传入类型
//类模板
template<class NameType, class AgeType>
class Person
{
public:
Person(NameType name, AgeType age)
{
this->mName = name;
this->mAge = age;
}
void PrintPerson()
{
cout << "Name:" << this->mName << " Age:" << this->mAge << endl;
}
public:
NameType mName;
AgeType mAge;
};
//1. 类模板做函数参数,指定传入类型
void doWork1(Person<string,int>& p)
{
p.mAge += 20;
p.mName += "_vip";
p.PrintPerson();
}
//2. 类模板做函数参数,参数模板化
template<class T1, class T2>
void doWork2(Person<T1, T2>& p)
{
// 查看T1、T2的数据类型
cout << "T1的数据类型:" << typeid(T1).name() << endl;
cout << "T2的数据类型:" << typeid(T2).name() << endl;
p.mAge += 20;
p.mName += "_vip";
p.PrintPerson();
}
//3. 类模板做函数参数,整个类模板化
template<class T>
void doWork3(T &p)
{
cout << "T的数据类型:" << typeid(T).name() << endl;
p.mAge += 20;
p.mName += "_vip";
p.PrintPerson();
}
int main()
{
Person<string, int> p("John", 30);
doWork1(p);
doWork2(p);
doWork3(p);
return 0;
}
3 类模板碰到继承的问题
3.1 类模板派生普通类
必须要指定出父类中的T数据类型,才能给子类分配内存
//类模板
template<class T>
class MyClass
{
public:
MyClass(T property)
{
this->mProperty = property;
}
public:
T mProperty;
};
//子类实例化的时候需要具体化的父类,子类需要知道父类的具体类型是什么样的
//这样c++编译器才能知道给子类分配多少内存
//普通派生类
class SubClass : public MyClass<int>
{
public:
SubClass(int b) : MyClass<int>(20)
{
this->mB = b;
}
public:
int mB;
};
3.2 类模板派生类模板
可以指定出父类中的T数据类型,也可以再用子类模板中的一个T2数据类型代表父类模板中的T数据类型
//父类类模板
template<class T>
class Base
{
T m;
};
// 1 指定出父类中的T数据类型
template<class T >
class Child1 : public Base<double> //继承类模板的时候,必须要确定基类的大小
{
public:
T mParam;
};
// 2 用子类模板中的一个T2数据类型代表父类模板中的T数据类型
template<class T1, class T2>
class Child2 : public Base<T2>
{
public:
T mParam;
};
void test01()
{
Child1<int> d1;
Child2<int,double> d1;
}
4 类模板的类内类外实现
4.1 类模板的类内实现
template<class NameType, class AgeType>
class Person
{
public:
// 类模板的类内实现
Person(NameType name, AgeType age)
{
this->mName = name;
this->mAge = age;
}
// 类模板的类内实现
void showPerson()
{
cout << "name: " << this->mName << " age: " << this->mAge << endl;
}
public:
NameType mName;
AgeType mAge;
};
void test01()
{
//Person P1("德玛西亚",18); // 类模板不能进行类型自动推导
Person<string, int>P1("德玛西亚", 18);
P1.showPerson();
}
4.2 类模板的类外实现
template<class T1, class T2>
class Person
{
public:
// 类内声明
Person(T1 name, T2 age);
void showPerson();
public:
T1 mName;
T2 mAge;
};
// 相比较普通的类,类外实现多了
// 1. template<class T1, class T2>
// 2. 类名后添加<T1,T2>
// 类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
this->mName = name;
this->mAge = age;
}
template<class T1, class T2>
void Person<T1, T2>::showPerson()
{
cout << "Name:" << this->mName << " Age:" << this->mAge << endl;
}
void test01()
{
//Person P1("德玛西亚",18); // 类模板不能进行类型自动推导
Person<string, int>P1("德玛西亚", 18);
P1.showPerson();
}
5 类模板的头文件和源文件分离问题
// Person.h文件
#pragma once
template<class T1,class T2>
class Person{
public:
Person(T1 name,T2 age);
void ShowPerson();
public:
T1 mName;
T2 mAge;
};
// Person.cpp文件
#include "Person.h"
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
this->mName = name;
this->mAge = age;
}
template<class T1, class T2>
void Person<T1, T2>::ShowPerson()
{
std::cout << "Name:" << this->mName << " Age:" << this->mAge << std::endl;
}
// main.cpp文件
#include <iostream>
#include "Person.h"
using namespace std;
int mian()
{
Person<string, int> p("Obama", 20);
p.ShowPerson();
return 0;
}
出现问题:
- 在Linux如果只包含Person.h头文件,那么会报错链接错误。
问题原因:
- 类模板需要二次编译,在出现模板的地方编译一次,在调用模板的地方再次编译。
- C++编译规则为独立编译。
解决方案:
- 类模板的声明和实现放到一个文件中,我们把这个文件命名为.hpp(这个是个约定的规则,并不是标准,必须这么写).
//解决方法 Person.hpp文件
#pragma once
template<class T1,class T2>
class Person{
public:
// 声明
Person(T1 name,T2 age);
void ShowPerson();
public:
T1 mName;
T2 mAge;
};
// 实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
this->mName = name;
this->mAge = age;
}
template<class T1, class T2>
void Person<T1, T2>::ShowPerson()
{
std::cout << "Name:" << this->mName << " Age:" << this->mAge << std::endl;
}
// main.cpp文件
#include <iostream>
#include "Person.hpp"
using namespace std;
int mian()
{
Person<string, int> p("Obama", 20);
p.ShowPerson();
return 0;
}
6 类模板碰到友元函数
6.1 友元函数类内实现
#include <iostream>
#include <cstring>
using namespace std;
template<class T1, class T2>
class Person
{
// 友元函数类内实现
friend void printPerson(Person<T1,T2> &p)
{
cout << "名字: " << p.m_Name << " 年龄: " << p.m_Age << endl;
}
public:
Person(T1 name, T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
private:
T1 m_Name;
T2 m_Age;
};
int main()
{
Person<string, int> p("小明", 18);
printPerson(p);
return 0;
}
6.2 友元函数类外实现
#include <iostream>
#include <cstring>
using namespace std;
// 告诉编译器这个类模板是存在的
template<class T1, class T2>
class Person;
// 告诉编译器这个函数模板是存在
template<class T1,class T2>
void printPerson(Person<T1,T2> &p);
/*
* 或者可以将printPerson友元函数实现放在此处
{
cout << "名字: " << p.m_Name << " 年龄: " << p.m_Age << endl;
}
*/
template<class T1, class T2>
class Person
{
// 友元函数类内声明,注意添加空模板列表<>, 因为友元函数是个模板函数
friend void printPerson<>(Person<T1,T2> &p);
public:
Person(T1 name, T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
private:
T1 m_Name;
T2 m_Age;
};
// 友元函数类外实现
template<class T1,class T2>
void printPerson(Person<T1,T2> &p)
{
cout << "名字: " << p.m_Name << " 年龄: " << p.m_Age << endl;
}
int main()
{
Person<string, int> p("小明", 18);
printPerson(p);
return 0;
}