C++模版【通俗易懂】

一、模版

模版就是建立通用的工具,大大提高复用性。

1.1、函数模版

作用:建立通用函数,其函数返回值类型和形参类型可以不具体指定,用一个虚拟的类型来指定

// 语法:
template <typename T>
  • template ---- 模版的声明,声明创建的模版

  • typename ---- 表示后面的符号是一种数据类型,可以使用class代替

  • T ---- 通用的数据类型,名称可以替换,通常为大写字母

//  定义通用格式的两数交换
template <typename T>       // 声明一个模版,告诉编辑器后面的代码紧跟着的T不要报错,T表示通用格式
void swap(T &a, T &b){
    T temp = a;
    a = b;
    b = temp;
}

调用格式:

/*
 * 使用函数模版
 * 1、自动类型推导:自动类型推导需要推导出一至的数据类型才可以使用
 * 2、显示指定类型
 */
int main(){
    int a = 10;
    int b = 30;
    // 自动类型推导
    swep(a ,b);
    cout << "a = " << a << " , b = " << b << endl;
    //显示指定类型
    double aa = 23.3,bb = 55.4;
    swep<double>(aa,bb);
    cout << "aa = " << aa << " , bb = " << bb << endl;
    return 0;
}

1.2、普通函数和模版的区别

普通函数和函数模版的区别:

  • 普通函数调用时可以发现自动类型转换(隐式类型转换)

  • 函数模版调用时,如果类型自动类型推导,就不会发生隐式类型转换

  • 如果利用显示指定类型的方式,可以发生隐式类型转换

#include <iostream>
using namespace std;
​
// 定义普通函数
int my_add(int a, int b){
    return a + b;
}
​
// 定义函数模版
template <typename T>
T myAdd(T a, T b){
    return a + b;
}
​
int main(){
    // 调用普通函数
    cout  << my_add('c',23) << endl;            // 会发生隐式类型转换,将字符行转换为整数进行计算
​
    // 自动类型推导调用函数模版
    cout << myAdd(23,3) << endl;
    //cout  << myAdd('c',23) << endl;                 // 报错,不会将字符型自动转换为整形
​
    // 显示指定类型调用函数模版
    cout << myAdd<int>('c',23) << endl;         // 会发生隐式类型转换,将字符行转换为整数进行计算
    return 0;
}

1.3、调用规则

普通函数和函数模版的调用规则:

  • 如果函数模版和普通函数都可以实现,优先调用普通函数

  • 可以通过空模版参数列表来强制调用函数模版

  • 函数模版也可以发生重载

  • 如果函数模版可以产生更好的匹配,优先调用函数模版

#include <iostream>
using namespace std;
​
// 定义普通函数
void print_show(int a, int b){
    cout << "普通函数的调用!"  << endl;
}
// 定义函数模版
template<typename T>
void print_show(T a, T b){
    cout << "函数模版的调用!" << endl;
}
// 定义重载的函数模版
template<typename T>
void print_show(T a, T b,T c){
    cout << "重载函数模版的调用!" << endl;
}
​
int main(){
    int a = 23, b = 24, c = 12;
    print_show(a,b);                        // 优先调用普通函数
​
    // 通过空模版参数列表,强制调用函数模版
    print_show<>(a,b);                      // 模版不指定类型,为空
    
    // 调用重载的函数模版
    print_show(a,b,c);
    return 0;
}

1.4、模版的局限性

模版并不是万能的,有些特定的数据类型,需要用具体的方式做特定的实现,c++提供函数模版的重载,如果模版中传入的是自定义数据类型,就需要模版的重载

#include <iostream>
#include <string>
using namespace std;
​
// 定义比较的模版
template <typename T>
void cmoputer(T &a,T &b){
    if(a == b){
        cout << "相等!" << endl;
    }else{
        cout << "不相等!" << endl;
    }
}
​
// 定义类
class Person{
public:
    string name;
    int age;
    Person(string name,int age){
        this->name = name;
        this->age = age;
    }
};
​
//定义重载的比较模版---处理自定义类型的比较
template<>          // 类型留空,因为后面指定了具体的类型
void cmoputer(Person& p1,Person &p2){
    if(p1.name == p2.name && p1.age == p2.age){
        cout << "相同!" << endl;
    }else{
        cout << "不相等!" << endl;
    }
}
​
int main(){
    // 调用模版
    int a = 23, b = 2;
    cmoputer(a,b);
    Person p1 = Person("zhang",23);
    Person p2 = Person("xianmi",12);
    // 调用模版(传入自定义类型)---- 传入自定义类型时需要定义重载的函数模版
    cmoputer(p1,p2);
    return 0;
}

1.5、类模版

建立一个通用的类,类中的数据类型和成员可以不具体指定,用一个虚拟的类型来代替

#include <iostream>
#include <string>
using namespace std;
​
// 定义类模版
template <class NameType, class AgeType>
class Person{
public:
    NameType name;
    AgeType age;
    Person(NameType name, AgeType age){
        this->name = name;
        this->age = age;
    }
    // 打印函数
    void print_show(){
        cout << "name = " << this->name << " , age = " << this->age << endl;
    }
};
​
int main(){
    // 创建类对象 --- 使用类模版
    Person<string,int> p1("zhans",23);
    p1.print_show();
    return 0;
}

注意:类模版中的成员函数只有在调用的时候才会创建,定义的时候不会创建

1.6、类模版和函数模版的区别

主要区别:

  • 类模版没有自动类型推导的使用方式---只能进行显示指定类型

  • 类模版在模版参数列表中可以有默认的参数----可以在定义时指定类型

// 定义类模版
template <class NameType, class AgeType = int>
class Person{
public:
    NameType name;
    AgeType age;
    Person(NameType name, AgeType age){
        this->name = name;
        this->age = age;
    }
    // 打印函数
    void print_show(){
        cout << "name = " << this->name << " , age = " << this->age << endl;
    }
};

1.7、类模版对象做函数参数

// 定义模版类
template <class t1, class t2>
class Person{
public:
    t1 m_name;
    t1 m_age;
    Person(t1 name, t2 age){
        this->m_name = name;
        this->m_age = age;
    }
    void print_show(){
        cout << "name = " << this->m_name << " , age = " << this->m_age << endl;
    }
};

类模版实例化出的对象,向函数参数传参的方式:

  • 指定传入的类型 ---- 直接显示对象的数据类型

    // 定义函数参数为类模版对象(指定类模版类型)
    void printPerson1(Person<string , int> &p1){
        p1.print_show();
    }
    ​
    int main(){
        // 创建类对象
        Person<string , int> p1("zhang",23);
        // 调用全局函数
        printPerson1(p1);
        return 0;
    }
  • 参数模版化 ---- 将对象中的参数变为模版进行传递

    // 参数模版化
    template <typename T1,typename T2>
    void printPerson2(Person<T1,T2> &p2){
        p2.print_show();
        // 查看模版推出的类型 ---- typeid(模版参数.name()
        cout << "T1的类型:" << typeid(T1).name()  << endl;
        cout << "T2的类型:" << typeid(T2).name()  << endl;
    }
    ​
    int main(){
        // 定义类模版对象
        Person<string , int> p2("zhang",25);
        // 调用参数模版化的全局函数
        printPerson2(p2);
        return 0;
    }
    
  • 整个类模版化 ---- 将这个对象类型模版化进行传递

    // 整个类模版化
    template <class T>
    void printPerson3(T &p){
        p.print_show();
    }

1.8、类模版与继承

当类模版碰到继承时:

  • 当子类继承的父类是一个模版类时,子类在声明的时候需要指出父类中<T>的类型,不指定无法为子类分配内存

  • 如果想灵活指定子类中的类型,就需要将子类也定义为模版类型

#include <iostream>
using namespace std;
​
// 定义父类为模版类
template <class T>
class Base{
public:
    T m;
};
​
// 定义子类(继承父类--父类为模版类)------ 继承的同时需要指定父类中T的类型
class Son1: public Base<int>{
    
};
​
// 定义子类(继承父类--父类为模版类)------- 继承的同时不指定父类中T的类型,直接将子类定义为模版类
template <class T>
class Son2: public Base<T>{
    
};
​
int main(){
    // 创建子类1的对象
    Son1 s1;
    
    // 创建子类2的对象----因为子类2是模版类,创建时需要指定模版类型
    Son2<int> s2;
    return 0;
}

1.9、类模版成员函数类外实现

// 定义类函数模版
template <class T1, class T2>
class Person{
public:
    T1 m_name;
    T2 m_age;
    Person(T1 name, T2 age);            // 构造函数类内声明
    void show(T1 name, T2 age);         // 成员函数的声明
};
​
// 类外实现类模版的构造函数 --- 必须声明为类模版
template <class T1,class T2>
Person<T1 , T2>::Person(T1 name, T2 age) {
    cout << "Person类模版的构造函数的类外实现" << endl;
    this->m_name = name;
    this->m_age = age;
}
​
// 类外实现类模版的成员函数
template <class T1,class T2>
void Person<T1,T2>::show(T1 name, T2 age) {
    cout << "name = " << name << " , age = " << age << endl;
    cout << "Person类模版的成员函数的外部实现" << endl;
}

注意:类模版中的成员函数在运行的时候才会创建,如果进行分文件编写,在创建对象的时候会报错,解决方法直接导入.cpp原文件或者将.cpp文件中的内容和.h中的内容写到一起命名为.hpp文件----.hpp文件就表示类模版

1.10、类模版和友元

  • 全局函数类内实现 ---- 直接在类内声明友元即可

  • 全局函数类外实现 ---- 需要提前让编译器知道全局函数的存在

  • 23
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值