• C++ 提高编程


    1. 模板

    1.1 模板的概念

    1.2 函数模板

    1.C++另一种编程思想称为 泛型编程,主要利用的技术就是模板
    2.C++提供两种模板机制: 函数模板 和 类模板

    1.2.1 函数模板语法

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

    //语法
    //函数声明或定义
    template<typename T>
    
    • 1
    • 2
    • 3

    解释:
    template – 声明创建模板
    typename — 表明其后面的符号是一种数据类型,可以用class代替
    T ---- 通用的数据类型,,名称可以替换,通常为大写字母。

    #include
    #include 
    using namespace std;
    //函数模板
    //两个整形交互
    void swapInt(int &a,int &b){
    	int t = a;
    	a = b;
    	b = t;
    } 
    //两个浮点型交互
    void swapDou(double &a,double &b){
    	double t = a;
    	a = b;
    	b = t;
    }
    //函数模板
    template<typename T>	//声明一个模板,告诉编译器后面代码中紧跟的T不要报错,T是一个通用数据类型
    void mySwap(T &a,T &b){
    	T tmp = a;
    	a = b;
    	b = tmp;
    } 
    int main() {
    	int a=1,b=2;
    	//1.自动推导类型 
    	mySwap(a,b);
    	//2.显示指定类型 
    	mySwap<int>(a,b); 
    	return 0;
    }    
    
    • 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

    在这里插入图片描述

    1.2.2 函数模板注意事项

    注意事项:
    1.自动类型推导,必须推导出一致的数据类型T,才可以使用
    2.模板必须要确定出T的数据类型,才可以使用

    #include
    #include 
    using namespace std;
    //函数模板注意事项 
    template<typename T>	//typename可以替换为class没有区别 
    //1.自动类型推导,必须推导出一致的数据类型T,才可以使用
    void mySwap(T &a,T &b){
    	T tmp = a;
    	a = b;
    	b = tmp;
    }
    void test01(){
    	int a = 10;
    	int b = 20;
    	mySwap(a,b);
    	/*char c = 'c';
    	mySwap(a,c);//错误 推导不出一致的T类型 */
    	cout<<a<<"-"<<b<<endl;
    } 
    //2.模板必须要确定出T的数据类型,才可以使用
    template<class T>
    void func(){
    	cout<<"调用func"<<endl; 
    }
    void test02(){
    	//无法调用 
    	//func();
    	//确定T的类型,可以调用
    	func<int>(); 
    }
    int main() {
    	int a=1,b=2;
    	//1.自动推导类型 
    	mySwap(a,b);
    	//2.显示指定类型 
    	mySwap<int>(a,b); 
    	return 0;
    }    
    
    • 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

    1.2.3 函数模板案例

    在这里插入图片描述

    #include
    #include 
    using namespace std;
    //实现通用 对数组进行排序的函数
    //规则 从大到小
    //算法 选择排序
    //测试 char 数组,int 数组
    template<class T>
    void mySwap(T &a,T &b){
    	T tmp = a;
    	a = b;
    	b =tmp;
    } 
    //排序算法--选择排序 
    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){
    			//交换max和i元素
    			 mySwap(arr[max],arr[i]);
    		}
    	}
    } 
    //打印数组模板
    template<class T>
    void printArr(T arr[],int len){
    	for(int i=0;i<len;i++){
    		cout<<arr[i]<<" ";
    	}
    	cout<<endl;
    } 
    void test01(){
    	//测试char数组
    	char chaArr[] = "badcfe"; 
    	int num = sizeof(chaArr) / sizeof(char);
    	mySort(chaArr,num);
    	printArr(chaArr,num);
    } 
    void test02(){
    	int intArr[] = {7,5,1,5,8,9};
    	int num = sizeof(intArr) / sizeof(int);
    	mySort(intArr,num);
    	printArr(intArr,num);
    }
    int main() {
    	test01();
    	test02();
    	return 0;
    }    
    
    • 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

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

    在这里插入图片描述

    #include
    #include 
    using namespace std;
    //普通函数与函数模板区别
    
    //1.普通函数 调用可以发生隐式类型转换
    //2.函数模板 用自动类型推导,不可以发生隐式类型转换
    //3.函数模板 用显示指定类型,可以发生隐式类型转换 
    
    //普通函数
    int myAdd01(int a,int b){
    	return a+b;
    } 
    
    //函数模板
    template<class T>
    T myAdd02(T a,T b){
    	return a+b;
    } 
    
    void test01(){
    	//普通函数 
    	int a=2,b=3;
    	char c = 'c';
    	cout<<myAdd01(a,b)<<endl;	//正常 
    	//发生隐式转换 
    	cout<<myAdd01(a,c)<<endl;	//正常-隐式转换 
    	//模板函数
    	cout<<myAdd01(a,b)<<endl;	//正常 
    	cout<<myAdd01(a,c)<<endl;	//出错 
    	
    	//显示指定类型
    	 cout<<myAdd01<int>(a,c)<<endl;	//正常-可以隐式转换 
    }
    int main() {
    	test01();
    	test02();
    	return 0;
    }    
    
    • 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

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

    1.2.5 普通函数与函数模板调用规则

    在这里插入图片描述

    #include
    #include 
    using namespace std;
    //普通函数与函数模板调用规则 
    
    //1.若函数模板和普通函数都可调用,则优先调普通函数 
    //2.可以通过空模板参数列表 强制调用 函数模板 
    //3.函数模板 可以发生 函数重载
    //4.若函数模板可以产生更好的匹配,优先调用函数模板 
    
    void myPrint(int a,int b)
    {
    	cout<<"调用的是普通函数"<<endl; 
    }
    template<class T>
    void myPrint(T a,T b){
    	cout<<"调用的是模板函数"<<endl; 
    }
    template<class T>
    void myPrint(T a,T b,T c){
    	cout<<"调用的是重载的模板函数"<<endl; 
    }
    void test01(){
    	int a=2,b=4;
    	//调用的是 普通函数 
    	myPrint(a,b);
    	//通过空参数列表,强制调用函数模板
    	myPrint<>(a,b);
    	//函数模板重载 
    	myPrint<>(a,b,100);
    	//若函数模板可以产生更好的匹配,优先调用函数模板 
    	char c1='a',c2='b';
    	myPrint(c1,c2);	//调用模板,不用隐式转换 
    	
    }
    int main() {
    	test01();
    	test02();
    	return 0;
    }    
    
    • 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

    1.2.6 模板的局限性

    局限性:模板的通用性并不是万能的

    #include
    #include 
    using namespace std;
    //模板局限性
    //模板不是万能的,有些特定数据类型,需要具体化方式做特殊实现
    
    class Person{
    public:
    	Person(string n,int a){
    		this->name = n;
    		this->age = a;
    	}
    	string name;
    	int age;
    };
    //对比两个数据是否相等 
    template<class T>
    bool myCompare(T &a,T &b){
    	if(a==b){
    		return true; 
    	} 
    	return false;
    }
    void test01(){
    	int a=10,b=20;
    	bool ret = myCompare(a,b);
    	if(ret){
    		cout<<"a==b"<<endl;
    	}else{
    		cout<<"a!=b"<<endl;
    	}
    }
    //利用具体化Person的版本实现代码,具体化优先调用 
    template<> bool myCompare(Person &a,Person &b){
    	if(a.name==b.name && a.age == b.age){
    		return true; 
    	} 
    	return false;
    }
    void test02(){
    	Person p1("Tom",12);
    	Person p2("Tom",12);
    	bool ret = myCompare(p1,p2);
    	if(ret){
    		cout<<"p1==p2"<<endl;
    	}else{
    		cout<<"p1!=p2"<<endl;
    	}
    }
    int main() {
    	test01();
    	test02();
    	return 0;
    }    
    
    • 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

    总结:
    1.利用具体化的模板,可以解决自定义类型的通用化
    2.学习模板并不是为了写模板,而是在STL能够运用系统提供的模板

    1.3 类模板

    1.3.1 类模板语法

    类模板作用:建立一个通用类,类中的成员 数据类型可以不具体制定,用一个虚拟的类型来代表。

    //语法
    template<typename T>
    
    • 1
    • 2

    解释:
    1.template – 声明创建模板
    2.typename – 表明其后面的符号是一种数据类型,可以用class代替

    #include
    #include 
    using namespace std;
    //类模板 
    template<class NameType,class AgeType>
    class Person{
    public:
    	Person(NameType n,AgeType a){
    		this->name = n;
    		this->age = a;
    	}
    	void showPer(){
    		cout<<this->name<<"-"<<this->age<<endl;
    	}
    	NameType name;
    	AgeType age;
    };
    void test01(){
    	//指定数据类型 
    	Person<string,int> p1("王",23);
    	p1.showPer();
    }
    
    int main() {
    	test01();
    	return 0;
    }    
    
    • 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

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

    1.3.2 类模板与函数模板区别

    主要区别两点:
    1.类模板没有自动类型推导的使用方式
    2.类模板在模板参数列表中可以有默认参数

    #include
    #include 
    using namespace std;
    //类模板 与 函数模板区别 
    template<class NameType,class AgeType = int>
    class Person{
    public:
    	Person(NameType n,AgeType a){
    		this->name = n;
    		this->age = a;
    	}
    	void showPer(){
    		cout<<this->name<<"-"<<this->age<<endl;
    	}
    	NameType name;
    	AgeType age;
    };
    //1.类模板没有自动类型推导使用方式
    void test01(){
    	//指定数据类型 
    	//Person p1("王",23);	//错误,编译器不会自动推导 
    	Person<string,int> p1("王",23);	//正确,只能用显示指定类型 
    	p1.showPer();
    }
    //2.类模板 在模板参数列表中可以有默认参数
    void test02(){
    	// template 改为 template 
    	//下边的int不需要写,也可以 
    	Person<string> p1("王",23);	
    	p1.showPer();
    }  
    int main() {
    	test01();
    	test02();
    	return 0;
    }    
    
    • 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

    总结:1.类模板使用只能用显示指定类型方式
    2.类模板中的模板参数列表可以有默认参数

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

    类模板中成员函数和普通类中成员函数创建时机有区别:
    1.普通类中的成员函数一开始就可以创建
    2.类模板中的成员函数在调用时才创建

    #include
    #include 
    using namespace std;
    //类模板中成员函数创建时机
    //类模板中成员函数在调用时才创建
    class Person1{
    public:
    	void showPer1(){
    		cout<<"showPer1 show"<<endl;
    	}
    }; 
    class Person2{
    public:
    	void showPer2(){
    		cout<<"showPer2 show"<<endl;
    	}
    }; 
    template<class T>
    class MyClass{
    public:
    	//因为该成员函数,没有调用时候不会创建,因此可以编译通过 
    	T obj;
    	void func1(){
    		obj.showPer1();
    	}
    	void func2(){
    		obj.showPer2();
    	}
    };
    
    void test01(){
    	MyClass<Person1> m1;
    	//调用func1的时候,才会创建MyClass的成员,此时有 Person1 obj, 
    	m1.func1();	//成功 
    	//m1.func2();	//失败 
    	MyClass<Person2> m2;
    	m2.func2();	//成功 
    }
    
    int main() {
    	test01();
    	return 0;
    }    
    
    • 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

    1.3.4 类模板对象做函数参数

    学习目标:类模板实例化出的对象,向函数传参数的方式
    一共有三种传入方式:
    1.指定传入的类型 — 直接显示对象的数据类型
    2.参数模板化 – 将对象中的参数变为模板进行传递
    3.整个类模板化 – 将这个对象类型 模板化进行传递

    #include
    #include 
    using namespace std;
    //类模板对象做函数参数
    template<class T1,class T2>
    class Person{
    public:
    	Person(T1 n,T2 a){
    		this->name = n;
    		this->age = a;
    	}
    	void showPer(){
    		cout<<this->name<<" "<<this->age<<endl;
    	}
    	T1 name;
    	T2 age;
    }; 
    //1.指定传入类型 
    void printPerson1(Person<string,int> &p){
    	p.showPer();
    }
    void test01(){
    	Person<string,int> p("孙悟空",100);
    	printPerson1(p);
    }
    //2.参数模板化
    template<class T1,class T2>
    void printPerson2(Person<T1,T2> &p){
    	p.showPer();
    	cout<<"T1的数据类型"<<typeid(T1).name()<<endl; 
    	cout<<"T2的数据类型"<<typeid(T2).name()<<endl; 
    }
    void test02(){
    	Person<string,int> p("猪八戒",100);
    	printPerson2(p);
    } 
    //3.整个类模板化
    template<class T>
    void printPerson3(T &p){
    	p.showPer();
    	cout<<"T的数据类型"<<typeid(T).name()<<endl; 
    }
    void test03(){
    	Person<string,int> p("唐僧",24);
    	printPerson3(p);
    } 
    
    int main() {
    	test01();test02();test03();
    	return 0;
    }    
    
    • 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

    总结:
    1.通过类模板创建的对象,可以有三种方式向函数中进行传参
    2.使用比较广泛是第一种:指定传入的类型

    1.3.5 类模板与继承

    当类模板碰到继承,需要注意以下几点
    1.当子类继承的父类是一个类模板时,子类声明时候,要指定出父类中T的类型
    2.若不指定,编译器无法给子类分配内存
    3.若想灵活指定出父类中T的类型,子类也需要变为类模板。

    #include
    #include 
    using namespace std;
    //继承中的类模板
    template<class T>
    class Base{
    public:
    	T m;
    }; 
    //class Son : public Base{};	//错误,必须要知道父类中的类型,才能继承给子类 
    class Son : public Base<int>{
    };
    void test01(){
    	Son s;
    }
    //若想零或指定父类中T类型,子类也需要变类模板
    template<class T1,class T2>
    class Son2:public Base<T1>{
    public:
    	Son2(){
    		cout<<"T1类型:"<<typeid(T1).name()<<endl; 
    		cout<<"T2类型:"<<typeid(T2).name()<<endl; 
    	}
    	T1 obj;
    }; 
    void test02(){
    	//父类的T是char,子类额外的属性是int 
    	Son2<int,char> s2;
    }
    int main() {
    	test01();test02();
    	return 0;
    }    
    
    • 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

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

    1.3.6 类模板成员函数的类外实现

    学习目标:掌握类模板中成员函数类外实现

    #include
    #include 
    using namespace std;
    //类模板成员函数类外实现 
    template<class T1,class T2>
    class Person{
    public:
    	Person(T1 n,T2 a);
    	/*{
    		this->name = n;
    		this->age = a;
    	}*/
    	void showPer();
    	/*{
    		cout<name<<"-"<age<
    	T1 name;
    	T2 age;
    };
    //构造函数类外实现
    template<class T1,class T2>
    Person<T1,T2>::Person(T1 n,T2 a){
    		this->name = n;
    		this->age = a;
    } 
    //成员函数类外实现
    template<class T1,class T2>
    void Person<T1,T2>::showPer(){
    	cout<<this->name<<"-"<<this->age<<endl;
    }
    void test01(){
    	Person<string,int> p("TOM",34);
    	p.showPer();
    } 
    int main() {
    	test01();
    	return 0;
    }    
    
    • 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

    1.3.7 类模板分文件编写

    学习目标:掌握类模板成员函数分文件编写产生的问题以及解决方式
    问题:类模板中成员函数创建时机是在调用阶段,导致分文件编写时,连接不到
    解决:
    方式1:直接包含.cpp文件
    方式2:将声明和实现写到同一个文件中,并更改后缀为.hpp,hpp是约定的名称,不是强制

    方式1
    //person.h
    #pragma once
    #include
    #include 
    using namespace std;
    template<class T1,class T2>
    class Person{
    public:
    	Person(T1 n,T2 a);
    	void showPer();
    	T1 name;
    	T2 age;
    }; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    //person.cpp
    #include "person.h"
    
    template<class T1,class T2>
    Person<T1,T2>::Person(T1 n,T2 a){
    		this->name = n;
    		this->age = a;
    } 
    template<class T1,class T2>
    void Person<T1,T2>::showPer(){
    	cout<<this->name<<"-"<<this->age<<endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    //主函数
    #include
    #include 
    //单独包含person.h则无法运行 
    //#include "person.h"
    //单独包含person.h则可以运行,此时直接从cpp文件查找Person构造函数和成员函数
    #include "person.cpp"
    using namespace std;
    void test01(){
    	Person<string,int> p("TOM",34);
    	p.showPer();
    }
    int main() {
    	test01();
    	return 0;
    }    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    将.h和.cpp内容写到一起,后缀改为.hpp
    //person.hpp
    #include
    #include 
    using namespace std;
    //类模板分文件编写问题及解决
    template<class T1,class T2>
    class Person{
    public:
    	Person(T1 n,T2 a);
    	void showPer();
    	T1 name;
    	T2 age;
    }; 
    
    template<class T1,class T2>
    Person<T1,T2>::Person(T1 n,T2 a){
    		this->name = n;
    		this->age = a;
    } 
    template<class T1,class T2>
    void Person<T1,T2>::showPer(){
    	cout<<this->name<<"-"<<this->age<<endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    //主函数
    #include
    #include 
    //包含hpp即可 
    #include "person.hpp"
    using namespace std;
    void test01(){
    	Person<string,int> p("TOM",34);
    	p.showPer();
    }
    int main() {
    	test01();
    	return 0;
    }    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    总结:主流解决方法是第二种,将类模板成员函数写到一起,并将后缀名改为.hpp;

    1.3.8 类模板与友元

    学习目标:掌握类模板配合友元函数的类外和类内实现
    1.全局函数类内实现:直接在类内声明友元即可
    2.全局函数类外实现:需要提前让编译器知道全局函数的存在

    #include
    #include 
    using namespace std;
    //通过全局函数打印person信息 
    
    //2.全局函数 类外实现
    //2.1 先声明模板类的存在 
    template<class T1,class T2>
    class Person;
    //2.2 再声明 printPer2 的存在 
    template<class T1,class T2>
    void printPer2(Person<T1,T2> p){
    	cout<<p.name<<"-"<<p.age<<endl;
    } 
    
    template<class T1,class T2>
    class Person{
    	//1.全局函数 类内实现
    	//printPer相当于全局函数,然后做了Person类的友元 
    	friend void printPer(Person<T1,T2> p){
    		cout<<p.name<<"-"<<p.age<<endl;
    	} 
    	//2.全局函数 类外实现 
    	// 加空模板参数列表 
    	// 若全局函数 是类外实现,需要让编译器提前知道这个函数的存在 
    	friend void printPer2<>(Person<T1,T2> p);
    public:
    	Person(T1 n,T2 a){
    		this->name = n;
    		this->age = a;
    	}
    private:
    	T1 name;
    	T2 age;
    };
    //1.全局函数类内实现 -测试 
    void test01(){
    	Person<string,int> p("TOM",34);
    	printPer(p);
    }
    //2. 全局函数类外实现 -测试 
    void test02(){
    	Person<string,int> p("Jom",24);
    	printPer2(p);
    } 
    
    int main() {
    	test01();	test02();
    	return 0;
    }    
    
    • 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

    总结:建议全局函数做类内实现,用法简单,编译器可以直接识别

    1.3.9 类模板案例

    实现一个普通的数组类,要求如下:
    在这里插入图片描述

    在这里插入图片描述

    //数组.hpp
    #include
    #include 
    using namespace std;
    //实现数组类
    template<class T>
    class Myarray{
    public:
    	Myarray(int cap){
    		//cout<<"Myarray有参构造"<
    		this->m_cap = cap;
    		this->m_size = 0;
    		this->pAddress = new T[this->m_cap];
    	}
    	//因为有堆区数据,因此需要深拷贝
    	 Myarray(const Myarray& arr){
    	 	this->m_cap = arr.m_cap;
    	 	this->m_size = arr.m_size;
    	 	//cout<<"Myarray拷贝构造"<
    	 	//深拷贝
    		 this->pAddress = new T[arr.m_cap];
    		 //将arr中的数据都拷贝过来
    		 for(int i=0;i<this->m_size;i++){
    		 	this->pAddress[i] = arr.pAddress[i];
    		 } 
    	 }
    	 //opeartor = 防止浅拷贝   a=b=c
    	 Myarray& operator=(const Myarray& arr){
    	 	//cout<<"Myarray的operator调用"<
    	 	//先判断原来堆区是否有数据,有则先释放
    		 if(this->pAddress != NULL){
    		 	delete[] this->pAddress;
    		 	this->m_cap = 0;
    	 		this->m_size = 0;
    		 } 
    		 //深拷贝
    		this->m_cap = arr.m_cap;
    	 	this->m_size = arr.m_size;
    	 	this->pAddress = new T[arr.m_cap];
    	 			 //将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_cap == this->m_size){
    		 	return;
    		 } 
    		 this->pAddress[this->m_size] = val;	//数组末尾插入数据
    		 this->m_size++; 	//更新数组大小 
    	 } 
    	 //尾删法
    	 void PopBack(){
    	 	//用户访问不到最后一个元素,即尾删
    		 if(this->m_size==0) return;
    		 this->m_size--; 
    	 }
    	 //通过下标访问数组元素 arr[0]
    	T& operator[](int index){
    		return this->pAddress[index];
    	}
    	//返回数组容量
    	int getCap(){
    		return this->m_cap;
    	} 
    	//返回数组大小
    	int getSize(){
    		return this->m_size;
    	} 
    	~Myarray(){
    		//cout<<"Myarray析构"<
    		if(this->pAddress != NULL){
    			delete[] pAddress;
    			pAddress = NULL;
    		}
    	}
    private:
    	//数组,指针指向堆区开辟的真实数组 
    	T* pAddress;
    	int m_cap; //数组容量
    	int m_size;	//数组大小 
    	
    }; 
    //main.cpp
      #include
    #include 
    using namespace std;
    #include "数组类1.hpp" 
    
    void test01(){
    	Myarray<int> arr1(5);		//有参构造测试 
    	Myarray<int> arr2(arr1);	//拷贝构造测试 
    	Myarray<int> arr3(100);
    	arr3 = arr1;
    }
    void printArr(Myarray<int>& arr1){
    	for(int i=0;i<arr1.getSize();i++){
    		cout<<arr1[i]<<" ";
    	}
    	cout<<endl;
    }
    void test02(){
    	Myarray<int> arr1(5);
    	//尾插法向数组插入数据
    	for(int i=0;i<5;i++){
    		arr1.PushBack(i);
    	}	
    	cout<<"arr1打印输出为:"<<endl; 
    	printArr(arr1);
    	cout<<"arr1容量为:"<<arr1.getCap()<<endl; 
    	cout<<"arr1大小为:"<<arr1.getSize()<<endl; 
    	Myarray<int> arr2(arr1);
    	cout<<"arr2尾删后打印输出为:"<<endl;
    	arr2.PopBack();
    	printArr(arr2);
    }
    //测试自定义数据类型
    class Person{
    public:
    	Person(){
    	};
    	Person(string n,int a){
    		this->age = a;
    		this->name = n;
    	}
    	int age;
    	string name;
    }; 
    void printPersonArr(Myarray<Person>& arr1){
    	for(int i=0;i<arr1.getSize();i++){
    		cout<<"姓名:"<<arr1[i].name<<" 年龄:"<<arr1[i].age<<endl; 
    	}
    }
    void test03(){
    	Myarray<Person> arr1(10);
    	Person p1("孙悟空",353);
    	Person p2("韩信",153);
    	Person p3("赵云",33);
    	Person p4("安琪",233);
    	
    	//数组插入数组中
    	arr1.PushBack(p1);arr1.PushBack(p2);arr1.PushBack(p3);arr1.PushBack(p4);
    	printPersonArr(arr1);
    	cout<<"personArr容量为:"<<arr1.getCap()<<endl;
    	cout<<"personArr大小为:"<<arr1.getSize()<<endl;
    }
    int main() {
    	test03();
    	return 0;
    }    
    
    • 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
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
  • 相关阅读:
    pandas dataframe 怎么保留重复的行
    @Resource和@Autowired的区别
    pycharm无法加载第三方库问题解决
    机器学习理论公式推导及原理—决策树
    CSS关于默认宽度
    MySQL update正在执行中突然断电,数据是否更改成功?
    educoder_python:4-1-逻辑控制(if)灯光开关,解一元二次方程,输入分数输出等级
    干货 | 深度多元时序模型在携程关键指标预测场景下的探索应用
    从策略到执行:实施战略定位的实战手册
    5年时间,从外包测试到自研,最后到阿里,这5年的经历只有自己能知道....
  • 原文地址:https://blog.csdn.net/weixin_43917045/article/details/138164975