c++函数模板和类模板

一、函数模板

  • 定义形式:

    template <typename type1,typename type2,...> return-type func-name(parameter list)
    {
       // 函数的主体
    } 

    说明:

    1. type1和type2…是占位符,用来泛指任意类型,如T,K…。
    2. template和typename是关键字;
  • 函数模板的调用:

    1. 显示类型调用
    2. 自动类型推导

    例子:

    #include <iostream>
    
    using namespace std;
    
    // 开始泛型编程
    template <typename T>
    
    void myswap(T &a,T &b){
        T c = 0;
        c = a;
        a = b;
        b = c;
    }
    
    void testInt(){
        int a = 1;
        int b = 2;
        // 显示调用函数模板
        myswap<int>(a,b);
        cout << "a = " <<a<< " b = "<<b<<endl;
    
        // 自动类型退导
        myswap(a,b);
        cout << "a = " <<a<< " b = "<<b<<endl;
    }
    
    void testChar(){
        char a = 'a';
        char b = 'b';
    
        // 显示调用函数模板
        myswap<char>(a,b);
        cout << "a = " <<a<< " b = "<<b<<endl;
    
        // 自动类型退导
        myswap(a,b);
        cout << "a = " <<a<< " b = "<<b<<endl;
    }
    
    int main01(){
        testInt();
        testChar();
    }
    
  • 函数模板强化:对字符数组或int数组进行排序

    // 对字符数组 int数组进行排序
    #include <iostream>
    
    using namespace std;
    
    template <typename T,typename K>
    void mySort(T *arr,K size){
        T tmp;
        for(int i=0;i<size;i++){
            for(int j = i+1;j<size;j++){
                if(arr[i]>arr[j]){
                    tmp = arr[i];
                    arr[i] = arr[j];
                    arr[j] = tmp;
                }
            }
        }
    }
    
    template <typename T,typename K>
    void printArr(T *arr,K size){
        for(int i=0;i<size;i++){
            cout << arr[i] << "  ";
        }
        cout << endl;
    }
    
    void testIntArr(){
        int arr[] = {12,34,5,7,91,23,45,11,8};
        int size = sizeof(arr)/sizeof(*arr);
        cout << "排序前..." <<endl;
        printArr(arr,size);
        mySort<int,int>(arr,size);
        cout << "排序后..." <<endl;
        printArr(arr,size);
    }
    
    void testCharArr(){
        char arr[] = {'b','a','e','c','d','g','h'};
        int size = sizeof(arr)/sizeof(*arr);
        cout << "排序前..." <<endl;
        printArr(arr,size);
        mySort<char,int>(arr,size);
        cout << "排序后..." <<endl;
        printArr(arr,size);
    }
    
    int main02(){
        testIntArr();
        testCharArr();
    }
    
  • 函数模板遇上函数重载

    1. 函数模板函数的调用(本质:类型参数化):将严格的按照类型进行匹配,不会进行自动类型转换;

    2. 普通函数的调用可能会进行隐式类型转换。

    例子:

    #include <iostream>
    
    using namespace std;
    
    template <typename T>
    void func(T a,T b){
        cout << "模板函数调用..." <<endl;
    }
    
    void func(int a,char b){
        cout << "普通函数调用..." <<endl;
    }
    
    int main(){
        int a = 10;
        char b = 'b';
    
        func(a,b); // 调用普通函数
        func(b,a); // 调用普通函数
        func(a,a); // 调用模板函数
    }
    
  • 函数模板和普通函数在一起的调用规则:

    1. 函数模板不允许自动类型转化;
    2. 普通函数能够进行自动类型转化(如果精确匹配到);

    3. 函数模板可以像普通函数一样被重载;

    4. c++编译器优先考虑普通函数;
    5. 如果模板函数可以产生一个更好的匹配,那么选择模板函数;
    6. 可以通过空模板实参列表的语法限定编译器只通过模板匹配。

    例子:

    #include <iostream>
    
    using namespace std;
    
    int Max(int a,int b){
        cout << "普通函数--Max(int a,int b)..." <<endl;
        return a > b? a:b;
    }
    
    template <typename T>
    T Max(T a,T b){
        cout << "模板函数--T Max(T a,T b)..." <<endl;
        return a > b? a:b;
    }
    
    template <typename T,typename K>
    T Max(T a,K b){
        cout << "模板函数--T Max(T a,K b)..." <<endl;
        return a > b? a:b;
    }
    
    template <typename T>
    T Max(T a,T b,T c){
        cout << "模板函数--int Max(T a,T b,T c)..." <<endl;
        return a > b? (a>c?a:c):(b>c?b:c);
    }
    
    int main(){
        int a = 10;
        int b=7;
        char c = 'c';
    
        int max = Max(a,b); // 调用普通函数
        cout << "max = " << max <<endl;
    
        max = Max(a,c); // 调用模板函数,产生了更好的匹配
        cout << "max = " << max <<endl;
    
        c = Max<char>('a','b'); // 显示调用函数模板
        cout << "c = " << c <<endl;
    
        c = Max<char>('a','b','c'); // 显示调用函数模板--重载
        cout << "c = " << c <<endl;
    }
    
  • c++编译器模板机制剖析:

    • 原理:c++编译器在我们调用的时候添加了额外的函数(相应类型的函数);
    • 编译器并不是把模板函数处理成能够处理任意类型的函数;
    • 编译器从模板函数通过具体类型产生不同的函数;
    • 编译器会对模板函数进行二次编译:
      • 在声明的地方对模板代码本身进行编译;
      • 在调用的地方对参替数换后的代码进行编译。

二、 类模板

  • 定义形式

    template <typename type1,typename T2,...> class class-name {
    .
    .
    .
    }
  • 类模板做函数参数(需要类型具体化)

    例子:

    #include <iostream>
    
    using namespace std;
    
    
    template <typename T>
    // 模板类,本身是一个抽象类
    class TemplateC{
    private:
        T a;
    public:
        TemplateC (T a){
            this->a = a;
        }
    
        void print(){
            cout << "a = " << a <<endl;;
        }
    };
    
    void useTemplateC(TemplateC<int> &c){
        c.print();
    }
    
    int main(){
        TemplateC<int> c1(10),c2(20),c3(30);
        useTemplateC(c1);
        useTemplateC(c2);
        useTemplateC(c3);
        return 0;
    }
    
  • 类模板派生子类(普通类和类模板)

    代码:

    #include <iostream>
    
    using namespace std;
    
    template <typename T>
    // 模板类,本身是一个抽象类
    class TemplateC{
    protected:
        T a;
    public:
        TemplateC (T a){
            this->a = a;
        }
    
        void print(){
            cout << "a = " << a <<endl;;
        }
    };
    
    // 必须给定泛型类型,因为c++编译器要知道如何分配内存
    class B : public TemplateC<int>{
    private:
        int b;
    public:
        B(int a,int b) : TemplateC(a){
            this->b = b;
        }
    
        void printB(){
            cout << "a = " <<a<<endl;
            cout << "b = "<<b<<endl;
        }
    };
    
    int main(){
        B b(10,20);
        b.printB();
    }
    

类模板的几种写法

  • 所有类成员函数都写在类内部(推荐)

    代码:

    #include <iostream>
    
    using namespace std;
    
    template <typename T,typename K>
    class Complex{
        // 友元函数的函数体也可以写在类外部
        friend ostream & operator<<(ostream &out,Complex &c){
            out << c.a << "+" << c.b << "j" <<endl;
            return out;
        }
    public:
        Complex(T a,K b){
            this->a = a;
            this->b = b;
        }
    
        void print(){
            cout << a << "+" << b << "j" <<endl;
        }
    
        // 重载加法运算符
        Complex operator+(Complex c2){
            Complex tmp(this->a+c2.a,this->b+c2.b);
            return tmp;
        }
    
    private:
        T a;
        K b;
    };
    
    int main(){
        Complex<int,int> c1(1,2),c2(1,3);
        c1.print();
    
        Complex<int,int> c3 = c1 + c2;
    
        // 需要重载<<操作符,重载<< >>只能用友元函数
        cout << c3 <<endl;
    }
    
  • 所有类成员函数都写在类外部

    
    #include <iostream>
    
    
    
    using namespace std;
    
    template <typename T,typename K>
    class Complex{
        // 友元函数的函数体也可以写在类外部
        friend ostream & operator<<(ostream &out,Complex<T,K> &c);
    public:
        Complex(T a,K b);
    
        void print();
    
        // 重载加法运算符
        Complex operator+(Complex c2);
    
    private:
        T a;
        K b;
    };
    
    template <typename T,typename K>
    Complex<T,K>::Complex(T a,K b){
        this->a = a;
        this->b = b;
    }
    
    template <typename T,typename K>
    void Complex<T,K>::print(){
        cout << a << "+" << b << "j" <<endl;
    }
    
    template <typename T,typename K>
    Complex<T,K> Complex<T,K>::operator+(Complex c2){
        Complex tmp(this->a+c2.a,this->b+c2.b);
        return tmp;
    }
    
    template <typename T,typename K>
    ostream & operator<<(ostream &out,Complex<T,K> &c){
        out << c.a << "+" << c.b << "j" <<endl;
        return out;
    }
    
    int main(){
        Complex<int,int> c1(1,2),c2(1,3);
        c1.print();
    
        Complex<int,int> c3 = c1 + c2;
    
        // 需要重载<<操作符,重载<< >>只能用友元函数
        cout << c3 <<endl;
    }
  • 所有类成员函数都写在类内部(类的声明(h)和实现(cpp)分开)(推荐)

    Complex.h

    
    #pragma once // 保证该文件只会被包含一次
    
    
    #include <iostream>
    
    
    
    using namespace std;
    
    template <typename T,typename K>
    class Complex{
        // 友元函数的函数体也可以写在类外部
        friend ostream & operator<<(ostream &out,Complex<T,K> &c);
    public:
        Complex(T a,K b);
    
        void print();
    
        // 重载加法运算符
        Complex operator+(Complex c2);
    private:
        T a;
        K b;
    };

    Complex.cpp

    
    #include "Complex.h"
    
    
    template <typename T,typename K>
    Complex<T,K>::Complex(T a,K b){
        this->a = a;
        this->b = b;
    }
    
    template <typename T,typename K>
    void Complex<T,K>::print(){
        cout << a << "+" << b << "j" <<endl;
    }
    
    template <typename T,typename K>
    Complex<T,K> Complex<T,K>::operator+(Complex c2){
        Complex tmp(this->a+c2.a,this->b+c2.b);
        return tmp;
    }
    
    template <typename T,typename K>
    ostream & operator<<(ostream &out,Complex<T,K> &c){
        out << c.a << "+" << c.b << "j" <<endl;
        return out;
    }

    测试代码

    
    #include <iostream>
    
    
    #include "Complex.h"
    
    
    using namespace std;
    
    int main(){
        Complex<int,int> c1(1,2),c2(1,3);
        c1.print();
    
        Complex<int,int> c3 = c1 + c2;
    
        // 需要重载<<操作符,重载<< >>只能用友元函数
        cout << c3 <<endl;
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值