前语

类封装了特点和方法,而这些特点和方法都有他们自己的数据类型,在有些特殊场景,咱们希望咱们的类里的这些特点和方法的类型能够在运用的时分再指定,由于咱们并无法事先判别这个类的运用者会传什么类型给到这个类,例如,一个调集类,用户能够往调集中存恣意的数据类型,咱们不可能为用户或许传的数据类型都做一个完成。那样太麻烦了,所以就出现了类模板。

1.类模板

语法:

template<class T>
类

解说:
template: 声明创建的模版
typename: 表面这以后的类型是一种数据类型,让编译器不要报错
T:通用数据类型,称号能够自界说,一般为大写字母

留意:class 能够运用typename 代替,即声明模版时也可写成template<typename T>的方法,作用是一样的,仅仅有的人会在函数模版的状况下运用typename,在类模版下运用class.

示例:

#include<iostream>
#include<string>
using namespace std;
template<class NameType, class AgeType>
class Person
{
public:
    NameType mName;
    AgeType mAge;
    Person(NameType name,AgeType age){
        this->mName = name;
        this->mAge = age;
    }
    void showPerson(){
        cout << "name=>" << this->mName << " ,age=>"<<this->mAge<<endl;
    }
};
void test(){
    Person<string,int> p1("walt",28);
    p1.showPerson();
}
int main()
{
    test();
    return 0;
}

在上面的示例中,咱们界说了一个Person类,类中有name和age两个特点,咱们运用类模板的方法界说后能够运用类模版中界说的虚拟型来表明name和age的数据类型,调用的时分就能够传入任何想用的数据类型了,假如你想让姓名以Int型表明,你就传int值,如:9527也能够表明一个人姓名

注:此处的Person类仅仅作为演示运用,并不具备通用性

2.类模板和函数模版的差异

类模板和函数模板的差异有两点:
(1)类模板没有主动类型推导,函数模板有主动类型推导
(2)类模板在模板参数列表中能够有默许参数,而函数模板不可

两者的差异如下列所示:

#include<iostream>
using namespace std;
// 类模版和函数模版的差异
template<class NameType,class AgeType = int>
class Person
{
public:
    Person(NameType name,AgeType age){
        this->mName = name;
        this->mAge = age;
    }
    void showPerson()
    {
        cout << "name=>"<<mName << " ,age=>" << mAge<<endl;
    }
    NameType mName;
    AgeType mAge;
};
// 1,类模版没有主动类型推导的运用方法
void test()
{
    // Person p("walt",28);//过错,无法主动类型推导
    Person<string,int> p("walt",28);// 正确,只能运用显示指定类型
    p.showPerson();
}
// 2,类模版在模版参数列表中能够有默许参数
void test1()
{
    Person<string> p("zhong",29);
    p.showPerson();
}
int main()
{
    //test();
    test1();
    return 0;
}

3.类模板的运用

3.1 类模板目标作为函数参数

类模板目标作为函数参数有三种状况分别是指定传入类型,参数模板化和整个类模板化,其间指定传入类型最常用,具体的请看代码示例:

#include<iostream>
#include<string>
using namespace std;
// 类模版目标做函数参数
template<class T1,class T2>
class Person{
    public:
        Person(T1 name,T2 age){
            this->mName = name;
            this->mAge = age;
        }
        void showPerson()
        {
            cout<< "name=>"<< mName << " ,age=>" << mAge<<endl;
        }
        T1 mName;
        T2 mAge;
};
//1.指定传入类型: 最常用
void printPerson(Person<string,int> &p)
{
    p.showPerson();
}
// 2.参数模版化
template<class T1,class T2>
void printPerson1(Person<T1,T2> &p)
{
    p.showPerson();
    cout<<"type of T1 is: " << typeid(T1).name() << endl;
    cout<<"type of T2 is: " << typeid(T2).name() << endl;
}
// 3.整个类模版化
template<class T>
void printPerson2(T &p){
    p.showPerson();
    cout<< "the type of T is:" << typeid(T).name() << endl;
}
void test(){
    Person<string,int> p("walt",28);
    printPerson(p);
}
void test1(){
    Person<string,int> p("zhong",33);
    printPerson1(p);
}
void test2()
{
    Person<string,int> p("xianjian",29);
    printPerson2(p);
}
int main()
{
    //test();
    //test1();
    test2();
    return 0;
}

3.2 类模板与承继

当父类是类模板的时分,有必要知道父类中界说的类型,才能承继给子类,并且假如想要灵敏指定父类中的T类型,那么子类也需求变成类模板,请看示例代码:

#include<iostream>
using namespace std;
// 类模版与承继
template<class T>
class Base{
    T m;
};
//class Son : public Base{//过错,有必要知道父类中T的类型,才能承继给子类
class Son:public Base<int>{//正确
};
// 假如想要灵敏指定父类中的T类型,子类也需求变类模版
template<class T1,class T2>
class Son2:public Base<T2>{
    public:
    Son2()
    {
        cout<< "the type of T1 is: " << typeid(T1).name() <<endl;
         cout<< "the type of T2 is: " << typeid(T2).name() <<endl;
    }
    T1 obj; 
};
void test()
{
    Son s1;
}
void test1()
{
    Son2<int,char> s1;// int 给了T1,char 给了T2,给了父类
}
int main()
{
    test1();
    return 0;
}

3.3 类模板与友元

在C++ 中,外部想要拜访类中的私有特点时,能够运用友元的方法,及将拜访者添加friend关键字,标记为“朋友”。当拜访的大局函数在类的内部完成时,直接拜访就行,可是假如拜访私有特点的友元函数在类外完成时,需求做到首要加空模板参数列表
friend void printPerson1<>(Person<T1,T2> p);
然后让编译器提早知道这个类模版的存在,即在文件的最前面声明下类模板

template<class T1, class T2>
class Person;

代码示例:

#include<iostream>
using namespace std;
// 提早让编译器知道Person模版类的存在
template<class T1, class T2>
class Person;
// 经过大局函数 打印Person信息
template<class T1, class T2>
void printPerson1(Person<T1,T2> p)
{
    cout<<"out of class implement: name=>"<<p.mName << " ,age=>"<< p.mAge << endl;
}
template<class T1, class T2>
class Person
{
    // 大局函数类内完成
   friend void printPerson(Person<T1,T2> p)
    {
        cout<<"name=>"<<p.mName << " ,age=>"<< p.mAge << endl;
    }
    // 大局函数类外完成
    // 加空模版参数列表
    // 大局函数假如是类外完成,需求让编译器提早知道这个函数的存在
    friend void printPerson1<>(Person<T1,T2> p);
    public:
        Person(T1 name,T2 age){
            this->mName = name;
            this->mAge = age;
        }
    private:
        T1 mName;
        T2 mAge;
};
// 大局函数类内完成
void test()
{
    Person<string,int> p("Tom",13);
    printPerson(p);
}
// 大局函数的类外完成
void test1()
{
    Person<string,int> p("jerry",16);
    printPerson1(p);
}
int main()
{
    //test();
    test1();
    return 0;
}

3.4 类模板分文件编写

在某些场景下咱们需求将类模板的界说和完成分文件编写。类模板的分文件编写步骤为:

  1. 先将类模板的界说和完成放到一同,命名为xxx.hpp

例如本例中的Person.hpp

#include<iostream>
#include<string>
using namespace std;
template<class T1,class T2>
class Person
{
    public:
        Person(T1 name,T2 age);
        void showPerson();
        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()
{
    cout<<"name=>"<< mName << " ,age=>"<<mAge<<endl;
}
  1. 需求运用时,运用include关键字引进这个xxx.hpp这个文件就行

示例:

#include<iostream>
#include<string>
#include "header/Person.hpp"
using namespace std;
// 类模版分文件编写
void test()
{
    Person<string,int> p("jerry",18);
    p.showPerson();
}
int main()
{
    test();
    return 0;
}

总结

本文介绍了类模板的语法,类模板和函数模板的差异,以及类模板的运用,其间类模板的运用需求留意的是当类模板和承继,友元,函数的类内完成和类外完成,分文件编写一同运用的时分的留意点。本篇文章要点在于看懂,能运用类模板就行,不要求会写。函数模板和类模板常识的学习是为了更好的运用系统提供的模板进行开发。