Skip to content

9.模板

//另一种思想叫泛型编程,主要利用模板
//提供两种模板机制:函数模板和类模板
//使用函数模板注意事项
//自动类型推导,必须推导出一致的T
//模板必须要确定出T的数据类型,才可以使用(函数体里不使用,除非显式指定类型)
//普通函数和函数模板区别:
// 普通函数调用时可以发生自动类型转换(隐式类型转换)
// 函数模板调用时,如果使用自动类型推导,不能发生隐式类型转换
// 如果使用显式指定类型,能发生隐式类型转换
//函数模板和普通函数调用规则:
// 如果两者都能实现,优先调用普通函数
// 可以通过空模板函数列表来强制调用函数模板
// 函数模板也可以重载
// 如果函数模板发生更好的匹配,优先调用函数模板
//模板的通用性不是万能的,有些特定的类型需要特定的实现
//在有myCompare(T a, T b)之后,无法比较两个自定义数据类型,可以利用具体化的Person的版本的代码实现,具体化优先调用
//template<> bool myCompare(Person a, Person b){具体比较Person类型的实现}
//类模板:建立一个通用类,类中成员的数据类型可以不具体制定,用一个虚拟类型代表
//类模板和函数模板区别
// 1.类模板没有自动类型推导的方式
// 2.类模板在模板参数列表中可以有默认参数
//  template<class NameType, class AgeType = int>
//类模板中成员函数在调用时创建
//类模板函数作函数参数
// 1.指定传入类型
// 2.参数模板化
// 3.整个类模板化
//类模板与继承
// 1.当子类的父类是类模板时,子类在声明时,要指定出父类中T的类型
// 2.如果不指定,编译器无法分配内存
// 3.如果想灵活指出父类中T的类型,子类也需变为类模板
//类模板成员函数类外实现
//类模板成员函数分文件编写
// 1.包含.cpp文件
// 2.将.h和.cpp文件写到一起,改成.hpp文件
//类模板与友元

#include <iostream>
#include <string>

//类模板成员函数分文件编写
// 1.包含.cpp文件(实际很少这样去做)
//#include "person02.cpp"

// 2.将.h和.cpp文件写到一起,改成.hpp文件
#include "person02.hpp"

#include"my_array.hpp"

using namespace std;

//函数模板
template<typename T> //typename可以替换为class
void mySwap(T& a, T& b)
{
    T tmp = a;
    a = b;
    b = tmp;
}

//通用数组排序
//规则:从大到小
//算法:选择排序
template<typename T>
void mySort(T& arr, int len)
{
    for (int i = 0; i < len; i++)
    {
        int max = i;
        for (int j = i + 1; j < len; j++)
        {
            if (arr[max] < arr[j])max = j;
        }
        if (max != i)
        {
            mySwap(arr[max], arr[i]);
        }
    }
}

template<typename T>
void printArray(T arr[], int len)
{
    for (int i = 0;i < len;i++)
    {
        cout << arr[i] << " ";
    }
    cout << endl;
}

void test01()
{
    //char数组
    char charArr[] = "badcfe";
    mySort(charArr, sizeof(charArr)/sizeof(char));
    printArray(charArr, sizeof(charArr) / sizeof(char));
    //int数组
    int intArr[5] = { 4,3,2,1,5 };
    mySort(intArr, sizeof(intArr) / sizeof(int));
    printArray(intArr, sizeof(intArr) / sizeof(int));
}

//类模板
template<typename NameType, typename AgeType>
class Person
{
public:
    Person(NameType name, AgeType age) :m_Age(age), m_Name(name) {}
    void showPerson()
    {
        cout << "Name: " << this->m_Name << " Age:" << this->m_Age << endl;
    }
    NameType m_Name;
    AgeType m_Age;
};

void test02()
{
    Person<string, int> p1("张三", 18);
    p1.showPerson();
}

//类模板对象作函数参数

template <class T1, class T2>
class Member
{
public:
    T1 m_Name;
    T2 m_Age;
    Member(T1 name, T2 age) :m_Name(name), m_Age(age) {}
    void showMember()
    {
        cout << "姓名:" << this->m_Name << " 年龄:" << this->m_Age << endl;
    }
};

void func1(Member<string,int>& m)
{
    //1.指定传入类型
    m.showMember();
}

template <typename T1, typename T2>
void func2(Member<T1,T2> m)
{
    //2.参数模板化
    m.showMember();
    cout << "T1的类型为" << typeid(T1).name() << endl;
    cout << "T2的类型为" << typeid(T2).name() << endl;
}

template <typename T>
void func3(T m)
{
    //3.整个类模板化
    m.showMember();
}

void test03()
{
    Member<string, int> m("张三", 18);
    func1(m);
    func2(m);
    func3(m);
}

//类模板成员函数类外实现
template<class T1, class T2>
class Person01
{
public:
    Person01(T1 name, T2 age);
    void showPerson();
    T1 m_Name;
    T2 m_Age;
};
template<class T1, class T2>
Person01<T1,T2>::Person01(T1 name, T2 age)
{
    this->m_Name = name;
    this->m_Age = age;
}

template<class T1, class T2>
void Person01<T1, T2>::showPerson()
{
    cout << "姓名:" << m_Name << " 年龄:" << m_Age << endl;
}

//template<class T1, class T2>
//class Person02
//{
//public:
//    Person02(T1 name, T2 age);
//    void showPerson();
//    T1 m_Name;
//    T2 m_Age;
//};
//template<class T1, class T2>
//Person02<T1, T2>::Person02(T1 name, T2 age)
//{
//    this->m_Name = name;
//    this->m_Age = age;
//}
//
//template<class T1, class T2>
//void Person02<T1, T2>::showPerson()
//{
//    cout << "姓名:" << m_Name << " 年龄:" << m_Age << endl;
//}

void test04()
{
    Person02<string, int> p("张三", 18);
    p.showPerson();
}

//之前要告知编译器时模板类
template<class T1, class T2>
class Person03;

//全局函数类外实现
//上方时普通函数的声明,下方是函数模板的实现
template<class T1, class T2>
void printPerson(Person03<T1, T2> p)
{
    cout << "Name: " << p.m_Name << " Age: " << p.m_Age << endl;
}

//类模板与友元
template<class T1, class T2>
class Person03
{
    // 全局函数类内实现
    //friend void printPerson(Person03<T1, T2> p)
    //{
    //    cout << "Name: " << p.m_Name << " Age: " << p.m_Age << endl;
    //}

    //全局函数类外实现
    //加空模板参数列表
    //如果全局函数时类外实现,需要让编译器提前知道这个函数的存在
    friend void printPerson<>(Person03<T1, T2> p);
public:
    Person03(T1 name, T2 age) :m_Name(name), m_Age(age) {}
private:
    T1 m_Name;
    T2 m_Age;
};

void test05()
{
    Person03<string, int> p("Tom", 20);
    printPerson(p);
}

void printIntArray(MyArray<int>& arr)
{
    for (int i = 0;i < arr.getSize();i++)
    {
        cout << arr[i] << endl;
    }
}

//类模板案例
void test06()
{
    MyArray<int> arr1(5);
    //MyArray<int> arr2(arr1);
    //MyArray<int> arr3(3);
    //arr3 = arr1;
    for (int i = 0;i < 5;i++)
    {
        arr1.pushBack(i);
    }
    printIntArray(arr1);
    cout << "arr1_capacity: " << arr1.getCapacity() << " arr1_size: " << arr1.getSize() << endl;
    MyArray<int> arr2(arr1);
    printIntArray(arr2);
    arr2.popBack();
    printIntArray(arr2);
    cout << "arr2_capacity: " << arr2.getCapacity() << " arr2_size: " << arr2.getSize() << endl;
}


int main()
{
    //int a = 10, b = 20;
    ////两种使用方式
    ////1.自动类型推导
    //myswap(a, b);
    //cout << "a = " << a << endl;
    //cout << "b = " << b << endl;
    ////2.显示指定类型
    //myswap<int>(a, b);
    //cout << "a = " << a << endl;
    //cout << "b = " << b << endl;
    test06();
    system("pause");
    return 0;
}

my_array.hpp

#pragma once
#include<iostream>
using namespace std;

template <class T>
class MyArray
{
public:
    MyArray(int capacity) :m_capacity(capacity), m_size(0), pAddress(new T[capacity]) { cout << "MyArray的有参构造" << endl; }
    MyArray(const MyArray& arr)
    {
        cout << "MyArray的拷贝构造" << endl;
        this->m_capacity = arr.m_capacity;
        this->m_size = arr.m_size;

        //深拷贝
        this->pAddress = new T[arr.m_capacity];
        //拷贝arr中的数据
        for (int i = 0; i < this->m_size;i++)
        {
            this->pAddress[i] = arr.pAddress[i];
        }
    }
    MyArray& operator=(MyArray& arr)
    {
        cout << "MyArray的operator=重载" << endl;
        //先判断原来堆区是否有数据,如果有,需要释放
        if (this->pAddress != nullptr)
        {
            delete[] this->pAddress;
            this->pAddress = nullptr;
            this->m_capacity = 0;
            this->m_size = 0;
        }
        this->m_capacity = arr.m_capacity;
        this->m_size = arr.m_size;

        //深拷贝
        this->pAddress = new T[arr.m_capacity];
        //拷贝arr中的数据
        for (int i = 0; i < this->m_size;i++)
        {
            this->pAddress[i] = arr.pAddress[i];
        }
        return *this;
    }
    void pushBack(const T&val)
    {
        if (this->m_capacity == this->m_size)
        {
            return;
        }
        this->pAddress[this->m_size] = val;
        this->m_size++;
    }
    void popBack()
    {
        if (this->m_size == 0) return;
        this->m_size--;
    }
    T& operator[](int index)
    {
        return this->pAddress[index];
    }
    int getCapacity() 
    {
        return this->m_capacity;
    }
    int getSize()
    {
        return this->m_size;
    }
    ~MyArray() 
    {
        if (this->pAddress != nullptr)
        {
            cout << "MyArray的析构函数" << endl;
            delete[] this->pAddress;
            this->pAddress = nullptr;
        }
    }
private:
    T* pAddress;
    int m_capacity;
    int m_size;
};


person02.h

#pragma once
#include<iostream>
using namespace std;

template<class T1, class T2>
class Person02
{
public:
    Person02(T1 name, T2 age);
    void showPerson();
    T1 m_Name;
    T2 m_Age;
};


person02.cpp

#include"person02.h"
template<class T1, class T2>
Person02<T1, T2>::Person02(T1 name, T2 age)
{
    this->m_Name = name;
    this->m_Age = age;
}

template<class T1, class T2>
void Person02<T1, T2>::showPerson()
{
    cout << "姓名:" << m_Name << " 年龄:" << m_Age << endl;
}


person02.hpp

#pragma once
#include<iostream>
using namespace std;

template<class T1, class T2>
class Person02
{
public:
    Person02(T1 name, T2 age);
    void showPerson();
    T1 m_Name;
    T2 m_Age;
};

template<class T1, class T2>
Person02<T1, T2>::Person02(T1 name, T2 age)
{
    this->m_Name = name;
    this->m_Age = age;
}

template<class T1, class T2>
void Person02<T1, T2>::showPerson()
{
    cout << "姓名:" << m_Name << " 年龄:" << m_Age << endl;
}