C++ 提高编程 黑马教程(05)

1.模板

(1)简介

cpp 复制代码
/*
 * C++提高编程
 * 本阶段主要针对C++泛型编程 和 STL技术 做详细讲解,探讨C++更深层的使用
 *
 * 1.模板
 * 概念:模板就是建立通用的模具,大大提高复用性
 *
 * 特点:
 *      模板不可以直接使用,它只是一个框架
 *      模板的通用并不是万能的
 */
int main() {

    return 0;
}

(2)函数模板

cpp 复制代码
/*
 * 函数模板
 * 建立一个通用函数,其函数返回值类型和形参类型可以不具体指定,用一个虚拟的类型来代表。
 * 语法:template<typename T>
 *      函数声明或者定义
 *      解释:template--声明创建模板
 *          tempname --表明其后面的符号是一种数据类型,可用class替换
 *          T --通用的数据类型,名称可以替换,通常为大写字母
 *
 *  隐式模板 mySwap(a,b);
 *  显示模板 mySwap<int>(a,b);
 *
 * 注意事项
 *      自动类型推导,需要推导出一致的数据类型T,才可以使用
 *      模板必须要确定出T的数据类型,才可以使用。
 */
//函数模板
template<typename T> //声明一个模板,告诉编译器后面代码中紧跟着的T不要报错,T是一个通用数据类型
void mySwap(T &a,T &b){
    T temp=a;
    a=b;
    b=temp;
}
template<typename T>
void func(){
    cout<<"fun 调用"<<endl;
}
void test01(){
    int a=10;
    int b=20;
    //1.自动类型推导
    mySwap(a,b);
    //2.显示指定类型
    mySwap<int>(a,b);
    cout<<"a="<<a<<endl;
    cout<<"b="<<b<<endl;
}
int main() {
    test01();
    func<int>();
    return 0;
}

(3)函数模板案例

cpp 复制代码
/*
 * 函数模板案例
 *
 * 描述:
 *      利用函数模板封装一个排序的函数,可以对不同数据类型素组进行排序
 *      排序规则从大到小,排序算法为选择排序
 *      分别利用char数组和int数组进行测试
 *
 */

template<class 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++){
            //认定的最大值比遍历出的数组要小,说明j下表的数值大
            if(arr[max]<arr[j]){
                max=j;
            }
        }
        if(max!=i){
            T temp=arr[max];
            arr[max]=arr[i];
            arr[i]=temp;
        }
    }
}

//冒泡排序
template<typename T>
void sayArray(T arr[],int len) {
    cout << "数组元素为:" << sizeof(arr) <<endl;
    for (int i = 0; i < len; i++) {
        cout << arr[i] << endl;
    }
}

void test01() {
    char charArr[]="abcdef";
    int num=sizeof (charArr)/ sizeof(char);
    mySort(charArr,num);
    sayArray(charArr,num);
}

int main() {
    test01();
    return 0;
}

(4)普通模板和函数模板的调用规则

cpp 复制代码
/*
 * 普通函数和函数模板的调用规则
 *      如果函数模板和普通函数都可以实现,优先调用普通函数
 *      可以通过空模板参数列表来强制调用函数模板  myPrint<>(2,2);
 *      函数模板也可以发生重载
 *      如果函数模板可以产生更好的匹配,优先调用函数模板
 */

void myPrint(int a, int b) {
    cout << "调用的普通函数" << endl;
}

template<typename T>
void myPrint(T a, T b) {
    cout << "调用的模板" << endl;
}

//函数模板 重载
template<typename T>
void myPrint(T a, T b, int) {
    cout << "调用的模板" << endl;
}

int main() {
    myPrint(1, 2);

    //空模板参数列表,强制调用函模板
    myPrint<>(2, 2);

    //如果函数模板产生更好的匹配,优先调用函数模板
    myPrint('1', 'a');
    return 0;
}

(5)模板的局限性

cpp 复制代码
/*
 * 模板的局限性
 * 模板的通用性并不是万能的
 * 有些特定数据类型,需要用具体化方式做特殊实现
 */

class Person {
public:
    string name;
    int age;

    Person(string name, int age) {
        this->name = name;
        this->age = age;
    }

    bool operator==(Person &p) {
        cout<<"operator== 函数被调用了"<<endl;
        if (this->name == p.name && this->age == p.age) {
            return true;
        } else {
            return false;
        }
    }
};

//对比两个数据是否相等
template<class T>
bool myCompare(T &a, T &b) {
    if (a == b) {
        return true;
    } else {
        return false;
    }
}

void test01() {
    int a = 20;
    int b = 20;
    bool ret = myCompare(a, b);
    if (ret) {
        cout << "a==b" << endl;
    } else {
        cout << "a!=b" << endl;
    }
}

void test02() {
    Person p1("张三", 10);
    Person p2("李四", 10);
    bool ret = myCompare(p1, p2);
    cout << "p1==p2:" << ret << endl;
}

int main() {

    test01();

    test02();

    return 0;
}

(7)类模板

(1)类模板开胃菜

cpp 复制代码
/*
 * 类模板
 * 类模板语法:
 * 建立一个通用类,类中的成员 数据类型可以不具体定制,用一个虚拟的类型来代表。
 *
 * 语法:
 *      template<typename T>
 *      类
 * 解释:
 *      template -- 声明创建模板
 *      typename -- 表明其后面的符号是一种数据类型,可以用class'代替
 *      T -- 通用的数据类型,名称可以替换,通常为大写字母
 *
 */

//类模板
template<class nameType, class ageType>
class Person {
public:
    nameType name;
    ageType age;

    Person(nameType name, ageType age) {
        this->name = name;
        this->age = age;
    }

    void showPerson() {
        cout << "name=" << this->name << ";age=" << age << endl;
    }
};

void test01() {
    Person<string, int> p1("孙大圣", 28);
    p1.showPerson();
    Person p2("孙大圣2", "282");
    p2.showPerson();
}

int main() {
    test01();
    return 0;
}

(2)类模板与函数模板的区别

cpp 复制代码
/*
 * 类模板和函数模板区别
 *      1.类模板没有自动类型推导的使用  自己使用的版本为C++20,以支持自动推导
 *      。类模板在模板参数列表中可以有默认参数
 *
 */

//类模板与函数模板的区别
template<class nameType, class ageType=string>
class Person {
public:
    nameType name;
    ageType age;

    Person(nameType name, ageType age) {
        this->name = name;
        this->age = age;
    }

    void showPerson() {
        cout << "name=" << this->name << ";age=" << age << endl;
    }
};

//1.类模板没有自动类型推导使用方式
void test01() {
    Person p1("孙悟空", 1000);
}

//2.类模板在模板参数列表中可以有默认参数
void test02() {
    Person p2("aaa", 123);
    p2.showPerson();
    Person p3("bbb", '234');
    p3.showPerson();
}

int main() {
    test02();
    return 0;
}

(3)类模板中成员函数创建时机

cpp 复制代码
/*
 * 类模板中成员函数创建时机
 *
 * 类模板中成员函数和普通类中成员函数创建时机是有区别的
 *      普通泪中的成员函数一开始就可以创建
 *      类模板中的成员函数在调用时候才创建
 *
 */

class Person1 {
public:
    void showPerson1() {
        cout << "Person1" << endl;
    }
};

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();
    }
};

void test01() {
    myClass<Person1> m;
    m.func1();
    //m.func2(); //报错,提示没有这个方法
    myClass<Person2> m2;
    m2.func2();
}

int main() {
    test01();
    return 0;
}

(4)类模板对象做函数参数

cpp 复制代码
*
 * 类模板对象做函数参数
 *
 * 学习目标:类模板实例化出的对象,向函数传参的方式
 *
 * 三种方式:
 *      1.指定传入的类型:直接显示对象的数据类型
 *      2.参数模板化:将对象中的参数变为模板进行传递
 *      3.整个类模板化:将这个对象类型 模板化进行传递
 *
 */

template<class T1, class T2>
class Person {
public:
    T1 name;
    T2 age;

    Person(T1 name, T2 age) {
        this->name = name;
        this->age = age;
    }

    void showPerson() {
        cout << "name=" << name << ";age=" << age << endl;
    }
};

//1.指定传入类型
void printPerson1(Person<string, int> &p) {
    p.showPerson();
}

void test01() {
    Person<string, int> p("孙悟空", 100);
    printPerson1(p);
}

template<class T1, class T2>
void printPerson2(Person<T1, T2> &p) {
    p.showPerson();
    cout << "T1类型为:" << typeid(T1).name() << endl;
    cout << "T2类型为:" << typeid(T1).name() << endl;
}

//2.参数模板花
void test02() {
    Person<string, int> p("猪八戒", 90);
    printPerson2(p);
}

template<class T>
void printPerson3(T p) {
    p.showPerson();
}

//3.整个类模板化
void test03() {
    Person<string, int> p("唐山", 20);
    printPerson3(p);
}

int main() {
    cout << "test01()" << endl;
    test01();
    cout << "test02()" << endl;
    test02();
    cout << "test03()" << endl;
    test03();
    return 0;
}

(5)类模板与继承

cpp 复制代码
/*
 * 类模板与继承
 *
 * 当类模板碰到继承时,需要注意:
 *      当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中的T的类型
 *      如果不指定,编译器无法给子类分配内存
 *      如果想灵活指定出父类中T的类型,子类也需要变为类模板
 *
 */

//类模板与继承
template<class T>
class Base {
    T m;
};

//class Son:public Base{ //错误,必须要知道父类中的T类型,才能继承给子类
//
//};

class Son : public Base<int> {

};

void test01() {
    Son son;
}

//如果想灵活指定父类中T类型,子类也需要变类模板
template<class T1, class T2>
class Son2 : public Base<T2> {
public:
    T1 obj;
    Son2() {
        cout << "T1类型为:" << typeid(T1).name() << endl;
        cout << "T2类型为:" << typeid(T2).name() << endl;
    }
};

void test02() {
    Son2<int, char> s2;
}

int main() {
    test01();
    test02();
    return 0;
}

(6)类模板成员函数类外实现

cpp 复制代码
/*
 * 类模板成员函数类外实现
 * 学习目标:能够掌握类模板中的成员函数类外实现
 */

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

//类模板 成员函数类外实现
template<class T1,class T2>
Person<T1,T2>::Person(T1 name,T2 age){
    this->name=name;
    this->age=age;
}
//成员函数的类外实现
template<class T1,class T2>
void Person<T1,T2>::showPerson(){
    cout<<"姓名:"<<name<<";年龄="<<age<<endl;
}



void test01(){
    Person<string,int> p("AAA",18);
    p.showPerson();
    Person p2("AAA",18);
    p2.showPerson();
}

int main() {
    test01();
    return 0;
}

(7)类模板与友元

cpp 复制代码
/*
 * 类模板与友元
 *
 * 学习目标:
 *      掌握类模板配合友元函数的类内和类外实现
 *  全局函数类内实现 -- 直接在类内声明友元即可
 *  全局函数类外实现 -- 需要提前让编译器知道全局函数的存在
 */

//让编译器提前知道Person类的存在
template<class T1, class T2>
class Person;

//类外实现
//加空模板参数列表
//如果全局函数 是类外实现,需要让编译器提前知道这个函数的存在
template<class T1, class T2>
void printPerson2(Person<T1, T2> p) {
    cout << "类外实现:姓名:" << p.name << ";年龄:" << p.age << endl;
}

//通过全局函数打印Perosn信息
template<class T1, class T2>
class Person {
    //全局函数 类内实现
    friend void printPerson(Person<T1, T2> p) {
        cout << "姓名:" << p.name << ";年龄:" << p.age << endl;
    }

    //全局函数 类外实现
    friend void printPerson2();

public:
    T1 name;
    T2 age;

    Person(T1 name, T2 age) {
        this->name = name;
        this->age = age;
    }
};

//1.全局函数类内实现
void test01() {
    Person p1("Tom", 18);
    printPerson(p1);
}

//1.全局函数类外实现
void test02() {
    Person p2("Dog", 22);
    printPerson(p2);
}

int main() {
    test01();
    test02();
    return 0;
}

(8)类模板案例

cpp 复制代码
未学习
https://www.bilibili.com/video/BV1et411b73Z?p=183
相关推荐
DK2215123 分钟前
机器学习系列-----主成分分析(PCA)
人工智能·算法·机器学习
oliveira-time29 分钟前
爬虫学习8
开发语言·javascript·爬虫·python·算法
星叔38 分钟前
ARXML汽车可扩展标记性语言规范讲解
java·前端·汽车
2401_8576009543 分钟前
SpringBoot框架:共享汽车管理的创新工具
java·spring boot·汽车
矛取矛求1 小时前
string接口的深度理解(内附思维导图)
c语言·开发语言·c++·接口·string
代码小鑫1 小时前
A15基于Spring Boot的宠物爱心组织管理系统的设计与实现
java·开发语言·spring boot·后端·毕业设计·宠物
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ1 小时前
mapper.xml 使用大于号、小于号示例
xml·java·数据库
点云侠1 小时前
二维椭圆拟合算法及推导过程
开发语言·c++·算法·计算机视觉·matlab
一直学习永不止步1 小时前
LeetCode题练习与总结:迷你语法分析器--385
java·数据结构·算法·leetcode·字符串··深度优先搜索
Dragonlongbo1 小时前
leetcode01 --- 环形链表判定
算法·leetcode·职场和发展