18_模板

模板

c++提供了函数模板(function template.)所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体制定,用一个虚拟的类型来代表。这个通用函数就成为函数模板。

凡是函数体相同的函数都可以用这个模板代替,不必定义多个函数,只需在模板中定义一次即可。在调用函数时系统会根据实参的类型来取代模板中的虚拟类型,从而实现不同函数的功能。c++提供两种模板机制:函数模板和类模1板类属-类型参数化,又称参数模板

函数模板

函数模板的定义

模板关键字template

#include <iostream>

using namespace std;

//T只能对当前函数有效, typename可以换成class
template<typename T> void swapAll(T &a, T &b)
{
    T tmp = a;
    a = b;
    b = tmp;
}

int main()
{
    int a = 10, b = 20;
    //函数调用时, 更具实参类型 会自动推导T的类型
    swapAll(a, b);
    cout << a << " " << b << endl;
    
    char a1 = 'a', b1 = 'b';
    swapAll(a1, b1);
    cout << a1 << " " << b1 << endl;
    
    return 0;
}

函数模板 会编译两次:

1对函数模板自身编译

2函数调用处 将T的类型具体化

函数模板目标: 模板是为了实现泛型, 可以减轻编译的工作量, 增强函数的重用性

函数模板的注意点

函数模板和普通函数都识别(优先选择普通函数)

template<class T> void swapAll(T &a, T &b)
{
    T tmp = a;
    a = b;
    b = tmp;
    cout << "函数模板" << endl;
}

void swapAll(int &a, int &b)
{
    int tmp = a;
    a = b;
    b = tmp;
    cout << "普通函数" << endl;
}

void main()
{
    int a = 10, b = 20;
    swapAll(a, b); //调用普通函数
}

函数模板 和 普通函数 都识别. 但强制使用函数模板

int main()
{
    int a = 10, b = 20;
    //强制使用函数模板
    swapAll<>(a, b); //调用函数模板
}

函数模板 自动类型推导 不能对函数的参数进行 自动类型转换

template<class T> void myPrintAll(T a, T b)
{
    cout << a << " " << b << endl;
    cout << "函数模板" << endl;
}

void myPrintAll(int a, int b)
{
    cout << a << " " << b << endl;
    cout << "普通函数" << endl;
}

int main()
{
    myPrintAll(10, 20); //普通函数
    myPrintAll('a', 'b'); //函数模板
    myPrintAll(10, 'b'); //普通函数 可以 自动类型转换
    //强制说明T为int类型 就支持自动类型转换
    myPrintAll<int>(10, 'b');
}

函数模板的重载

template<class T> void myPrintAll(T a)
{
    cout << a << endl;
    cout << "函数模板" << endl;
}

template<class T> void myPrintAll(T a, T b)
{
    cout << a << " " << b << endl;
    cout << "函数模板" << endl;
}

函数模板的局限性

当函数模板 推导出T为数组或其他自定义类型数据 可能导致运算符 不识别

解决方法

运算符重载(推荐)
#include <iostream>

using namespace std;

class Data
{
    friend ostream& operator<<(ostream& out, Data ob);
private:
    int data;
public:
    Data(){}
    Data(int data)
    {
        this->data = data;
    }
};

ostream& operator<<(ostream& out, Data ob)
{
    out << ob.data;
    return out;
}

template<class T> void myPrintAll(T a)
{
    cout << a << endl;
    cout << "函数模板" << endl;
}

int main()
{
    myPrintAll(10); //10
    myPrintAll('a'); //'a'
    Data ob1(100);
    myPrintAll(ob1); //100
}
具体化函数模板
#include <iostream>

using namespace std;

template<class T> void myPrintAll(T a)
{
    cout << a << endl;
    cout << "函数模板" << endl;
}

class Data
{
    friend void myPrintAll<Data>(Data ob);
private:
    int data;
public:
    Data(){}
    Data(int data)
    {
        this->data = data;
    }
};

//函数模板具体化
template<> void myPrintAll<Data>(Data ob)
{
    cout << ob.data << endl;
    cout << "函数模板具体化" << endl;
}

int main()
{
    myPrintAll(10);
    myPrintAll('a');
    Data ob(10);
    myPrintAll(ob);
    return 0;
}

类模板

类模板基本概念

有时,有两个或多个类,其功能是相同的,仅仅是数据类型不同。类模板用于实现类所需数据的类型参数化。

类模板的定义方式

#include <iostream>

using namespace std;

//类模板
template<class T1, class T2> class Data
{
private:
    T1 a;
    T2 b;
public:
    Data(){}
    Data(T1 a, T2 b)
    {
        this->a = a;
        this->b = b;
    }
    void showData()
    {
        cout << a << " " << b << endl;
    }
};

int main()
{
//    Data ob; //error 无参推导不出T的类型
    Data ob(10, 'c');
    Data<int, int> ob2(100, 'c');
    ob.showData();
    ob2.showData();
    return 0;
}

类模板的成员函数在类外实现

#include <iostream>

using namespace std;

template<class T1, class T2>
class Data
{
private:
    T1 a;
    T2 b;
public:
    Data(){}
    Data(T1 a, T2 b);
    void showData();
};

template<class T1, class T2>Data<T1, T2>::Data(T1 a, T2 b)
{
    this->a = a;
    this->b = b;
}

template<class T1, class T2>
void Data<T1, T2>::showData()
{
    cout << a << " " << b << endl;
}

int main()
{
    Data ob(100, 200);
    ob.showData();
    return 0;
}

函数模板作为类模板的友元

#include <iostream>

using namespace std;

//类模板
template<class T1, class T2>
class Data
{
    template<class T3, class T4>
    friend void myPrintData(Data<T3, T4> &ob);
private:
    T1 a;
    T2 b;
public:
    Data(){}
    Data(T1 a, T2 b);
    void showData();
};

//函数模板
template<class T3, class T4>
void myPrintData(Data<T3, T4> &ob)
{
    cout << ob.a << " " << ob.b << endl;
}

int main()
{
    Data<int, char> ob(100, 'a');
    myPrintData(ob);
    return 0;
}

template<class T1, class T2>
Data<T1, T2>::Data(T1 a, T2 b)
{
    this->a = a;
    this->b = b;
}

template<class T1, class T2>
void Data<T1, T2>::showData()
{
    cout << a << " " << b << endl;
}

普通函数作为类模板的友元

template<class T1, class T2>
class Data
{
    friend void myPrintData(Data<int, char> &ob);
private:
    T1 a;
    T2 b;
public:
    Data(){}
    Data(T1 a, T2 b);
    void showData();
};

void myPrintData(Data<int, char> &ob)
{
    cout << ob.a << " " << ob.b << endl;
}
int main()
{
   	Data<int, char> ob1(100, 'A');
    myPrintData(ob1);
}

模板头文件和源文件分离问题

模板. h文件 和 .cpp文件分离后main.cpp要都include才能运行,比较麻烦, 所以用.hpp包含这两部分

data.hpp

#ifndef DATA_H
#define DATA_H

#include<iostream>
using namespace std;

template<class T1, class T2>
class Data
{
private:
    T1 a;
    T2 b;
public:
    Data();
    Data(T1 a, T2 b);
    void showData();
};

template<class T1, class T2>Data<T1, T2>::Data()
{
    cout << "无参构造" << endl;
}

template<class T1, class T2>Data<T1, T2>::Data(T1 a, T2 b)
{
    this->a = a;
    this->b = b;
}

template<class T1, class T2>
void Data<T1, T2>::showData()
{
    cout << a << " " << b << endl;
}

#endif // DATA_H

main.cpp

#include <iostream>
#include"data.hpp"

using namespace std;

int main()
{
    Data ob(100, 2);
    ob.showData();
    return 0;
}

案例: 设计数组类模板

myarray.hpp

#ifndef MYARRAY_HPP
#define MYARRAY_HPP
#include<iostream>
#include<string.h>

using namespace std;

template<class T>
class MyArray
{
    template<class T1>
    friend ostream& operator<<(ostream& out, MyArray<T1>& ob);
private:
    T *arr;
    int size;
    int capacity;
public:
    MyArray();
    MyArray(int capacity);
    MyArray(const MyArray &ob);
    ~MyArray();
    MyArray& operator=(MyArray &ob);
    void pushBack(T element);
    void sortArray();
};

template<class T>
MyArray<T>::MyArray()
{
    capacity = 5;
    size = 0;
    arr = new T[capacity];
    memset(arr, 0, sizeof(T)*capacity);
}

template<class T>
MyArray<T>::MyArray(int capacity)
{
    this->capacity = capacity;
    size = 0;
    arr = new T[capacity];
    memset(arr, 0, sizeof(T)*capacity);
}

template<class T>
MyArray<T>::MyArray(const MyArray &ob)
{
    if(arr != NULL)
    {
        delete[] arr;
        arr = NULL;
    }
    size = ob.size;
    capacity = ob.capacity;
    arr = new T[capacity];
    memset(arr, 0, sizeof(T)*capacity);
    memcpy(arr, ob.arr, sizeof(T)*capacity);
}

template<class T>
MyArray<T>::~MyArray()
{
    if(arr != NULL)
    {
        delete[] arr;
        arr = NULL;
    }
}

template<class T>
MyArray<T>& MyArray<T>::operator=(MyArray &ob)
{
    if(arr != NULL)
    {
        delete[] arr;
        arr = NULL;
    }
    size = ob.size;
    capacity = ob.capacity;
    arr = new T[capacity];
    memset(arr, 0, sizeof(T)*capacity);
    memcpy(arr, ob.arr, sizeof(T)*capacity);
    return *this;
}

template<class T>
void MyArray<T>::pushBack(T element)
{
    if(size == capacity)
    {
        capacity = capacity*2;
        T *tmp = new T[capacity];
        memset(tmp, 0, sizeof(T)*capacity);
        if(arr != NULL)
        {
            memcpy(tmp, arr, sizeof(T)*capacity);
            delete[] arr;
        }
        arr = tmp;
    }
    arr[size] = element;
    size++;
}

template<class T>
void MyArray<T>::sortArray()
{
    if(size == 0)
    {
        cout << "容器没有数据" << endl;
    }
    else
    {
        for(int i=0;i<size-1;i++)
        {
            for(int j=i+1;j<size;j++)
            {
                if(arr[i] > arr[j])
                {
                    T tmp = arr[i];
                    arr[i] = arr[j];
                    arr[j] = tmp;
                }
            }
        }
    }
}

template<class T1>
ostream &operator<<(ostream &out, MyArray<T1> &ob)
{
    for(int i=0;i<ob.size;i++)
    {
        out << ob.arr[i] << " ";
    }
    return out;
}

#endif // MYARRAY_HPP

main.cpp

#include <iostream>
#include"myarray.hpp"
#include<string>

using namespace std;

class Person
{
    friend ostream& operator<<(ostream& out, Person& ob);
private:
    int num;
    string name;
    float score;
public:
    Person(){}
    Person(int num, string name, float score)
    {
        this->num = num;
        this->name = name;
        this->score = score;
    }
    bool operator>(Person& ob)
    {
        return num>ob.num;
    }
};

ostream& operator<<(ostream& out, Person& ob)
{
    out << ob.num << " " << ob.name << " " << ob.score;
    return out;
}

int main()
{
    MyArray<int> arr1;
    arr1.pushBack(20);
    arr1.pushBack(9);
    arr1.pushBack(80);
    arr1.pushBack(55);
    arr1.pushBack(79);
    arr1.pushBack(86);
    arr1.pushBack(15);
    arr1.pushBack(85);
    arr1.pushBack(100);
    arr1.pushBack(2);
    cout << arr1 << endl;
    arr1.sortArray();
    cout << arr1 << endl;

    MyArray<Person> arr2;
    arr2.pushBack(Person(3, "lucy", 98));
    arr2.pushBack(Person(1, "back", 45));
    arr2.pushBack(Person(7, "jack", 38));
    arr2.pushBack(Person(5, "wuh", 99));
    arr2.pushBack(Person(9, "lip", 85));
    arr2.sortArray();
    cout << arr2 << endl;
    return 0;
}

类模板的继承

类模板 派生出 普通类

#include <iostream>

using namespace std;

template<class T1, class T2>
class Base
{
private:
    T1 a;
    T2 b;
public:
    Base(){}
    Base(T1 a, T2 b);
    void showData();
};

template<class T1, class T2>
Base<T1, T2>::Base(T1 a, T2 b)
{
    this->a = a;
    this->b = b;
}

template<class T1, class T2>
void Base<T1, T2>::showData()
{
    cout << a << " " << b << endl;
}

//类模板派生出类模板
template<class T1, class T2, class T3>
class Son1: public Base<T1, T2>
{
public:
    T3 c;
public:
    Son1(T1 a, T2 b, T3 c): Base<T1, T2>(a, b){
        this->c = c;
    }
};

int main()
{
    Son1<int, char, int> ob1(100, 'A', 200);
    ob1.showData();
    cout << ob1.c << endl;
    return 0;
}

posted @ 2023-10-11 16:59  爱吃冰激凌的黄某某  阅读(8)  评论(0编辑  收藏  举报