• 第六部分--模板


    第六部分–模板

    1、模板的概念

    模板就是建立通用的模具,大大提高复用性

    image-20220822214315726

    image-20220822214347862

    模板的特点:

    模板不可以直接使用,他只是一个框架

    模板的通用并不是万能的

    2、函数的模板

    image-20220822214518197

    2.1 函数模板语法

    image-20220822215335064

    代码:

    #include
    using namespace std;
    
    //函数模板
    
    //两个整型交换函数
    
    void swapInt(int& a, int& b) {
    	int temp = a;
    	a = b;
    	b = temp;
    }
    
    //交换两个浮点型函数
    void swapDouble(double& a, double& b) {
    	double temp = a;
    	a = b;
    	b = temp;
    }
    
    
    
    //函数模板
    template//声明一个模板 高数编译器后面代码中紧跟着的T不要报错,T是一个通用数据类型
    void mySwap(T& a, T& b) {
    	T temp = a;
    	a = b;
    	b = temp;
    }
    
    void test01() {
    	int a = 10;
    	int b = 20;
    	/*swapInt(a, b);*/
    	//利用函数模板交换
    	//两种方式使用函数模板
    	//1、自动类型推导
    	mySwap(a, b);
    	//2、显示指定类型
    	mySwap(a, b);
    	cout << "a=" << a << endl;
    	cout << "b=" << b << endl;
    
    	double c = 2.1;
    	double d = 1.1;
    	swapDouble(c, d);
    	cout << "c=" << c << endl;
    	cout << "d=" << d << endl;
    }
    
    int main() {
    	test01();
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    image-20220822221044875

    2.2 函数模板注意事项

    image-20220901205744946

    image-20220901211132338

    image-20220901211244392

    总结:

    使用模板时必须确定出通用数据类型T,并且能够推导出一致的类型

    2.3 函数模板案例

    image-20220901211621896

    #include
    using namespace std;
    
    //实现通用  对数组进行排序的函数
    //规则  从大到小
    //算法  选择
    //测试  char  数组、int数组
    
    //交换算法
    template
    void mySwap(T& a, T& b) {
    	T temp = a;
    	a = b;
    	b = temp;
    }
    
    //排序算法
    template 
    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) {
    			mySwap(arr[max], arr[i]);
    		}
    	}
    }
    
    //提供打印数组模板
    template
    void printArray(T arr[], int len) {
    	for (int i = 0; i < len; i++) {
    		cout << arr[i] << "";
    	}
    	cout << endl;
    }
    
    void test01() {
    	//测试char数组
    	char charArr[] = "bcdfgsdx";
    	int num = sizeof(charArr) / sizeof(char);
    	mySort(charArr, num);
    	printArray(charArr,num);
    }
    
    void test02() {
    	//测试int 数组
    	int intArr[] = { 4,5,8,6,3,2,1,7,1 };
    	int num = sizeof(intArr) / sizeof(int);
    	mySort(intArr, num);
    	printArray(intArr, num);
    }
    
    int main() {
    	//test01();
    	test02();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    2.4 普通函数与函数模板的区别

    image-20220901215612096

    image-20220901220207099

    image-20220901220236522

    总结:建议使用显示指定类型的方式,调用函数模板,因为可以自己确定通用类型T

    2.5 普通函数与函数模板的调用规则

    image-20220901220411698

    image-20220901220745890

    image-20220901220802798

    总结:既然提供了函数模板,就不要提供一个普通函数,否则容易出现二义性

    2.6 模板的局限性

    image-20220902202706376

    image-20220902202753266

    image-20220902203439202

    image-20220902203521935

    image-20220902203547932

    image-20220902203613823

    总结:

    利用具体化的模板,可以解决自定义类型的通用化

    学习模板并不是为了写模板,而是在STL能够运用系统提供的模板

    3、类模板

    3.1 类模板的语法

    image-20220902203741222

    image-20220902204325464

    image-20220902204344249

    总结:类模板和函数模板语法相似,在声明模板template后面加类,此类称为类模板

    3.2 类模板与函数模板的区别

    image-20220902204803800

    #include
    using namespace std;
    
    //类模板与函数模板
    template //设置了默认参数类型,下面实例化可以不写
    class Person {
    public:
    	Person(NameType name, AgeType age) {
    		this->m_Name = name;
    		this->m_Age = age;
    	}
    	void showPerson() {
    		cout << "name:" << this->m_Name << "age = " << this->m_Age << endl;
    	}
    	NameType m_Name;
    	AgeType m_Age;
    };
    
    //1、类模板没有自动类别推导使用方式
    void test01() {
    	//Person p("孙悟空",1000);//错误,无法自动推导类型
    	Personp("孙悟空",1000);
    	p.showPerson();
    }
    //2、类模板在模板参数列表中可以有默认参数
    void test02() {
    	//Personp("猪八戒", 999);
    	Personp("猪八戒", 999);
    	p.showPerson();
    }
    
    int main() {
    	//test01();
    	test02();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    总结:

    类模板使用只能显示指定类型方式

    类模板中的模板参数列表可以有默认参数

    3.3 类模板中成员函数创建时机

    image-20220902210039552

    image-20220902210410353

    image-20220902210448026

    总结:类模板中的成员函数并不是一开始就创建的,在调用是才去创建

    3.4 类模板对象做函数参数

    image-20220902210558038

    image-20220902213105704

    image-20220902213138106

    image-20220902213154532

    总结:

    通过类模板创建的对象,可以有三种方式向函数中进行传参

    使用比较广泛是第一种:指定传入的类型

    3.5 类模板与继承

    image-20220902214123833

    总结:如果父类是类模板,子类需要指定出父类中T的数据类型

    3.6 类模板成员函数类外实现

    image-20220902214745029

    image-20220902214853795

    总结:类模板中成员函数类外实现时,需要加上模板参数列表

    3.7 类模板文件编写

    image-20220902215143840

    3.8 类模板与友元

    image-20220902215257067

    3.9 类模板案例

    image-20220902215659449

    image-20220902220035381

    头文件:

    MyArray.hpp

    //自己通用的数组类
    #pragma once
    #include
    using namespace std;
    
    template
    class MyArray {
    public:
    	//有参构造  参数 容量
    	MyArray(int capacity) {
    		//cout << "MyArray的有参构造" << endl;
    		this->m_Capacity = capacity;
    		this->m_Size = 0;
    		this->pAddress = new T[this->m_Capacity];
    
    	}
    
    	//尾插法
    	void Push_Back(const T & val) {
    		//判断容量是否等于大小
    		if (this->m_Capacity == this->m_Size) {
    			return;
    		}
    		this->pAddress[this->m_Size] = val;//在数组末尾插入数据
    		this->m_Size++;//更新数组大小
    	}
    
    	//尾删法
    	void Pop_Back() {
    		//让用户访问不到最后一个元素,即为尾删法,逻辑删除
    		if (this->m_Size == 0) {
    			return;
    		}
    		this->m_Size--;
    
    	}
    
    	//让用户可以通过下标的方式访问数组中的元素  arr[0]
    	T& operator[](int index) {
    		return this->pAddress[index];
    	}
    
    	//返回数组容量
    	int getCapacity() {
    		return this->m_Capacity;
    	}
    
    	int getSize() {
    		return this->m_Size;
    	}
    
    	//拷贝构造
    	MyArray(const MyArray& arr) {
    		//cout << "MyArray的拷贝构造" << endl;
    		this->m_Capacity = arr.m_Capacity;
    		this->m_Size = arr.m_Size;
    		//this->pAddress = arr.pAddress;
    
    		//深拷贝
    		this->pAddress = new T[arr.m_Capacity];
    
    		//将arr中的数据都拷贝过来
    		for (int i = 0; i < this->m_Size; i++) {
    			this->pAddress[i] = arr.pAddress[i];
    		}
    	}
    
    	//operator = 防止浅拷贝问题 a = b = c
    	MyArray& operator=(const MyArray& arr) {
    		//cout << "MyArray的operator构造" << endl;
    		//先判断原来堆区是否有数据,如果有先释放
    		if (this->pAddress != NULL) {
    			delete[] this->pAddress;
    			this->pAddress = NULL;
    			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];
    		for (int i = 0; i < this->m_Size; i++) {
    			this->pAddress[i] = arr.pAddress[i];
    		}
    		return *this;
    
    	}
    
    
    	//析构函数
    	~MyArray() {
    		//cout << "MyArray的析构构造" << endl;
    		if (this->pAddress != NULL) {
    			delete[] this->pAddress;
    			this->pAddress = NULL;
    		}
    	}
    
    private:
    
    	T* pAddress;
    	int m_Size;
    	int m_Capacity;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105

    源文件 test.cpp文件

    #include
    using namespace std;
    #include "MyArray.hpp"
    #include 
    
    void printIntArray(MyArray & arr) {
    	for (int i = 0; i < arr.getSize(); i++) {
    		cout << arr[i] << endl;
    	}
    }
    
    void test01() {
    	MyArray arr1(5);
    	for (int i = 0; i < 5; i++) {
    		//利用尾插法向数组中插入数据
    		arr1.Push_Back(i);
    
    	}
    	cout << "arr1的打印输出: " << endl;
    	printIntArray(arr1);
    	cout << "arr1的容量为:  " << arr1.getCapacity() << endl;
    	cout << "arr1的大小为: " << arr1.getSize() << endl;
    
    	MyArray arr2(arr1);
    	cout << "arr2的打印输出为: " << endl;
    	printIntArray(arr2);
    
    	//尾删
    	arr2.Pop_Back();
    	cout << "arr2的容量为:  " << arr2.getCapacity() << endl;
    	cout << "arr2的大小为: " << arr2.getSize() << endl;
    	/*MyArray arr2(arr1);
    	MyArray arr3(100);
    	arr3 = arr1;*/
    }
    
    //测试自定义类型数据
    class Person {
    public:
    
    	Person() {};
    	Person(string name, int  age) {
    		this->m_Age = age;
    		this->m_Name = name;
    	}
    	string m_Name;
    	int m_Age;
    
    };
    
    void printPersonArray(MyArray& arr) {
    	for (int i = 0; i < arr.getSize(); i++) {
    		cout << "姓名" << arr[i].m_Name << "年龄" << arr[i].m_Age << endl;
    	}
    }
    void test02() {
    	MyArray arr(10);
    
    	Person P1("孙悟空",999);
    	Person P2("韩信", 22);
    	Person P3("妲己", 33);
    	Person P4("赵云", 44);
    	Person P5("安其拉", 55);
    
    	//将数据插入到数组中
    	arr.Push_Back(P1);
    	arr.Push_Back(P2);
    	arr.Push_Back(P3);
    	arr.Push_Back(P4);
    	arr.Push_Back(P5);
    
    	//打印数组
    	printPersonArray(arr);
    	//输出容量
    	cout << "arr容量为:  " << arr.getCapacity() << endl;
    	cout << "arr大小为:   " << arr.getSize() << endl;
    
    }
    int main() {
    	test01();
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82

    总结:能够利用所学知识点实现通用的数组

  • 相关阅读:
    第一期 微信云开发小程序介绍-生活智打卡
    【云原生之kubernetes】在kubernetes集群下的jobs与cronjobs管理
    简单写一个eventbus
    Linux下gcc编译器和gdb调试
    LeGO-LOAM(1):运行LeGO-LOAM
    市场热度持续提升!4D成像雷达进入「细分场景争夺战」
    C++:模板(函数模板、类模板)
    ***杨辉三角_yyds_LeetCode_python***
    操作系统 —— 进程篇
    前端面试问题(3)
  • 原文地址:https://blog.csdn.net/charles_zhang_/article/details/126683251