一、引言
在写排序算法时,如果要写一个交换两个数据的函数,由于基本数据类型有int、float、double等等类型,所以针对每种数据类型可能我们要写很多swap函数,这些函数除了传入的数据类型不同,函数体结构大致相同。所以C++为了避免让程序员写很多大量重复代码,设计了一种叫做“模板”的东西。我们写程序时,先不指定什么类型,在调用时我们再说明一下是什么类型,具体怎么实现接着往下看。

二、函数模板
1、定义
像开头所说,如果要对int、double类型的两个数进行交换我们要写两个函数,但用函数模板时只需要写一个函数即可。模板定义如下:

template <typename T>
或者

template <class T>
其中,template是声明一个模板,typename是声明一个虚类型T,这个T可以代替int、double等基本数据类型,那为什么还有class?因为最初是用class声明一个T来表示通用数据类型,但考虑到class会和“类”混淆,就改用typename进行定义,同时保留了class,这两者效果相同,但我个人比较习惯用class。

在进行声明模板之后下面就开始写模板的函数体了,例如交换函数,合起来就是:

template <class T>
void swap(T *p1,T *p2){
 T temp=*p1;
 *p1=*p2;
 *p2=temp;
}

这样就是一个完整的函数模板

2、调用
有两种方式使用模板

(1)自动类型推导:编译器会自动判定你传入的数据是什么数据类型,然后将T改成对应数据类型进行操作;

(2)自己声明数据类型进行调用,具体实现如下:

    //1、自动类型推导
    swap(a, b);
    cout << "a=" << a << endl;
    cout << "b=" << b << endl;
    //2、显示指定类型
    swap<int>(a, b);
    cout << "a=" << a << endl;
    cout << "b=" << b << endl;

何时必须自己声明数据类型呢?

那就是没有参数传递的时候。比如知识一个打印函数,没有参数传递,这里就不写代码了。

3、多个虚类型
前面是针对一个函数里面都是同一数据类型的,如果含有不同数据类型,假如有两个:

template<class T1,class T2>
void func(T1 a,T2 b){....}
1
2
调用时可以自动识别类型也可以自己声明数据类型:

func<int,double>(1,3.14);
1
三、类模板
前面已经介绍了模板大致的作用,这里对类模板就不做过多说明,直接上干货:

1、定义
我们定一个学生类

template<class T1,class T2,class T3>
class Student{
public:
 Student(T1 name,T2 age,T3 score){
 ..........
 }
 T1 m_Name;
 T2 m_Age;
 T3 m_Score;
}
2、调用
调用时必须指定输入了什么数据,也就是尖括号不能省略,这点与函数模板不一样

Student<string,int,float>s("Tom",18,85.5);
1
四、类的函数模板
在类内定义的话就跟前面的函数模板一样,如果在类外定义,类外也要写上函数模板的形式:

template<class numType>
class Compare{
public:
 Compare(numType a,numType b){
 this->a=a;
 this->b=b;
 }
 //声明模板函数:
 numType max( );
private:
 numType a;
 numTypr b;
}
//类外定义模板函数
template<class numType>
numType Compare::max( ){
 return this->a>this->b?this->a:this->b;
 }

五、类作为数据类型传入
//定义Person1
class Person1 {
public:
    void showPerson1() {
        cout << "Person1 show" << endl;
    }
};
//定义Person2
class Person2 {
public:
    void showPerson2() {
        cout << "Person2 show" << endl;
    }
};
//定义类模板
template<class T>
class MyClass {
public:
    T obj;
    //类模板中的成员函数
    void func1() {
        obj.showPerson1();
    }
    void func2() {
        obj.showPerson2();
    }
};
//主函数
int main() {
    MyClass<Person1>m;
    m.func1();
    //m.func2();//会报错,因为“showPerson2”: 不是“Person1”的成员
    system("pause");
    return 0;
}

六、类模板与继承
如果父类是一个模板,子类继承父类模板的时候,不知道父类模板内存是多少,编译器就无法给子类分配内存,解决方案是给子类也加上模板,方案如下:

template<class T>
class Base {
    T m_Name;
};
//class Son :public Base { //错误,必须知道T的内存才能指定空间
class Son1:public Base<int>{ // 不灵活
};
//想要灵活指定父类中T的类型
template<class T1,class T2>
class Son2 :public Base<T2> {
public:
    T1 m_A;
};
int main() {
    //让子类的T为string,子类的T1为int
    Son2<int, string>s2;
    system("pause");
    return 0;
}

七、类模板与友元
前面也有过传入类对象到函数里面,如果这个函数需要用到对象里面的数据,而该数据有被设定为private(私有)就无法直接访问,此时又要用到友元函数的知识:

注意:类内添加友元函数后,如果没有对该友元函数进行声明,编译器认为你没有这个函数,会进行报错。

//先声明类和函数,防止编译器报错
template<class T1,class T2>
class Person;
template<class T1, class T2>
void printPerson(Person<T1, T2> p);

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 << "Name:" << p.m_Name << endl;
    cout << "Age:" << p.m_Age << endl;
}

觉得有用记得顶一下哦_
 

09-11 05:17