类与对象
一、C++模板
1.1 模板概论
C++提供了函数模板,所谓函数模板,实际上是建立一个通用函数,其函数类型和参数类型不具体制定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。凡是函数体相同的函数都可以用这个模板代替,不必定义多个函数,只需在模板中定义一次即可。在调用函数时系统会根据实参的类型来取代模板中的虚拟类型,从而实现不同函数的功能。C++提供两种模板机制:函数模板和类模板,类属-类型参数化,又称参数模板。
总结:模板把函数或要处理的数据类型参数化,表现为参数的多态性,称为类属。模板用于表达逻辑结构相同,但具体数据元素类型不同的数据对象的通用行为。
二、函数模板
C++特点:封装、继承、多态
C++特点:面向对象编程(封装继承多态)、泛型编程(模板)
案例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| void swapInt(int &a,int &b){ int tmp = a; a = b; b = tmp; return; } void swapChar(char &a,char &b){ char tmp = a; a = b; b = tmp; return; } void test01(void){ int data1 = 10,data2 = 20; cout<<"data1="<<data1<<",data2"<<data2<<endl; swapInt(data1,data2); cout<<"data1="<<data1<<",data2"<<data2<<endl;
char data3='a',data4='b'; cout<<"data3="<<data3<<",data4"<<data4<<endl; swapChar(data3,data4); cout<<"data3="<<data3<<",data4"<<data4<<endl; }
|
运行结果:
上面两个交换数据的函数只是数据类型不同,其他的都是一样的,这个时候我们可以使用函数模板,简化代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
template<typename T> void mySwap(T &a,T &b){ T tmp = a; a = b; b = tmp; return; } void test02(){ int data1 = 10,data2 = 20; cout<<"data1="<<data1<<",data2"<<data2<<endl; mySwap(data1,data2); cout<<"data1="<<data1<<",data2"<<data2<<endl;
char data3='a',data4='b'; cout<<"data3="<<data3<<",data4"<<data4<<endl; mySwap(data3,data4); cout<<"data3="<<data3<<",data4"<<data4<<endl; }
|
运行结果:
用模板是为了实现泛型,可以减轻编程的工作量,增加函数的重用性。
2.1 函数模板和普通函数的区别
- 当函数模板与普通函数同名时,优先使用普通函数;可以通过
<>
指定使用函数模板
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<typename T> void mySwap(T &a,T &b){ cout<<"函数模板"<<endl; T tmp = a; a = b; b = tmp; return; }
void mySwap(int &a, int &b){ cout<<"普通函数"<<endl; int tmp = a; a = b; b = tmp; return; } void test01(void){ int data1 = 10,data2 = 20; mySwap(data1,data2);
int data3 = 10,data4 = 20; mySwap<>(data3,data4);
return; }
|
运行结果:
- 函数模板的参数类型不能进行自动类型转换;如果想让函数模板进行类型转换,可以认为加
<类型>
指定要进行转换的类型。
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
| template<typename T> void mySwap(T a,T b){ cout<<"函数模板"<<endl; cout<<a<<","<<b<<endl; return; }
void mySwap(int a, int b){ cout<<"普通函数"<<endl; cout<<a<<","<<b<<endl; return; } void test01(void){ int a = 100; char b = 'a';
mySwap(a,b);
mySwap<int>(a,b); mySwap<char>(a,b);
return; }
|
运行结果:
2.2 函数模板总结
- 函数模板与普通函数的区别:函数模板不允许自动类型转换,普通函数能够进行自动类型转换
- 函数模板和普通函数在一起(同名)调用规则:C++编译器优先考虑普通函数,可以通过空模板实参列表的语法限定编译器只能通过模板匹配,函数模板可以像普通函数那样被重载,如果函数模板可以产生一个更好的匹配,那么选择模板。
- 模板实现机制:编译器并不是把函数模板处理成任何类型的函数,函数模板通过具体类型产生不同的函数,编译器会对函数模板进行两次编译,在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码进行编译。
2.3 函数模板的局限性
如果代码实现时定义了赋值操作,a=b,但是T为数据,这种假设就不成立了同样,如果里面的语句为判断语句if(a>b),但T如果是结构体,该假设也不成立,另外如果传入的是数组,数组名为地址,因此它比较的是滴啊之,而这也不是我们所希望的操作。总之,编写的模板函数很可能无法处理某些类型,另一方面,有时候通用化是有意义的,但C++语法不允许这样做。为了解决这种问题,C++提供模板函数的重载,为这些特定的类型提供具体化的模板。
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
| class Person{ public: int a; int b; public: Person(int a,int b){ cout<<"Person构造函数"<<endl; this->a = a; this->b = b; } };
template<typename T> T mySwap(T &a,T &b){ return a > b ? a : b; }
ostream& operator<<(ostream &out,Person &ob){ out<<ob.a<<" "<<ob.b; return out; }
void test01(){ int data1 = 10,data2 = 20; int t1 = mySwap(data1,data2); cout<<t1<<endl;
Person ob1 = Person(10,20); Person ob2 = Person(30,40);
}class Person{ public: int a; int b; public: Person(int a,int b){ cout<<"Person构造函数"<<endl; this->a = a; this->b = b; } };
template<typename T> T myMax(T &a,T &b){ return a > b ? a : b; }
ostream& operator<<(ostream &out,Person &ob){ out<<ob.a<<" "<<ob.b; return out; }
void test01(){ int data1 = 10,data2 = 20; int t1 = myMax(data1,data2); cout<<t1<<endl;
Person ob1 = Person(10,20); Person ob2 = Person(30,40);
}
|
运行结果:
上述问题的解决办法有两种:提供函数模板具体化;重载>运算符
方法1:
1 2 3 4
| template<> Person myMax(Person &ob1,Person &ob2){ return ob1.a > ob2.b ? ob1 : ob2; }
|
方法2:推荐这种,各自的管理各自的
1 2 3 4
| bool operator>(Person &ob){ return this->a > ob.b ? true : false; }
|
三、类模板
3.1 基本概念
类模板和函数模板的定义和使用类似,我们已经进行了介绍。有时,有两个或多个类,其功能是相同的,仅仅是数据类型不同。类模板用于实现类所需数据的类型参数化。
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
| #include <iostream> #include <string> using namespace std; template<class T1,class T2> class Person{ public: T1 name; T2 num; public: Person(T1 name,T2 num){ this->name = name; this->num = num; cout<<"有参构造函数"<<endl; } ~Person(){ cout<<"析构函数"<<endl; } void showPerson(){ cout<<"name="<<this->name<<",num="<<this->num<<endl; } }; void test01(){ string name = "小花"; int num = 100; Person<string,int> ob1 = Person<string,int>(name,num); ob1.showPerson();
Person<string,int> ob2 = Person<string,int>("小明",200); ob2.showPerson();
Person<string,int> ob3("小王",300); ob3.showPerson(); }
int main(int argc, char *argv[]) { test01(); return 0; }
|
运行结果:
注意:ob1 ob2 ob3是不同的类 Person与<string,int>结合表示一个类。
3.2 类模板做函数参数
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
| template<class T1,class T2> class Person{ public: T1 name; T2 num; public: Person(T1 name,T2 num){ this->name = name; this->num = num; cout<<"有参构造函数"<<endl; } ~Person(){ cout<<"析构函数"<<endl; } void showPerson(){ cout<<"name="<<this->name<<",num="<<this->num<<endl; } }; void fun(Person<string,int> &ob){ ob.name += "vip"; ob.num += 1000; return; } void test02(){ Person<string,int> ob("小花",200); fun(ob); ob.showPerson(); }
|
Person
类模板作为参数必须要具体化加<string, int>
,可以理解为Person <string,int>
才是一个类
3.3 类模板派生普通类
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
| template<class T1,class T2> class Person{ public: T1 name; T2 num; public: Person(T1 name,T2 num){ this->name = name; this->num = num; cout<<"有参构造函数"<<endl; } ~Person(){ cout<<"析构函数"<<endl; } void showPerson(){ cout<<"name="<<this->name<<",num="<<this->num<<endl; } }; class Student:public Person<string,int>{ public: int score; public: Student(string name,int num,int score):Person<string,int>(name,num){ this->score = score; cout<<"Student有参构造函数"<<endl; } ~Student(){ cout<<"Student析构函数"<<endl; } }; void test03(){ Student ob1("小明",300,70); ob1.showPerson(); }
|
运行结果:
Person
类模板作为基类必须要具体化加<string, int>
,可以理解为Person <string,int>
才是一个类