七、C++之泛型编程
一、函数模板
1.1 函数模板的基本使用
C++提供函数模板,所谓函数模板,实际上是建立一种通用函数,其函数类型和形参模型不具体制定,用一个虚拟的类型来代表,这个通用函数称为函数模板。凡是函数体相同的函数都可以用这个模板代替,不必定以多个函数类型,只需要在模板中定义一次即可,在调用函数时系统会根据实参的类型来取代模板的虚拟类型,从而实现不同函数的功能。
// 交换两个变量 当数据 /* template(class T) 告诉编译器紧跟着的出现的T不要报错。 1. 自动类型推导, 必须要有参数类型才能推导, 类型参数化,必须要传入 2. 显示指定类型, mySwap<int>(a, b); 3. 模板必须要指定T才能使用 */ #define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> #include<string> using namespace std; void mySwapInt(int &a, int &b) { int tmp; tmp = a; a = b; b = tmp; } void mySwapDouble(double &a, double &b) { double tmp; tmp = a; a = b; b = tmp; } // 类型,逻辑非常相似 // 类型参数化,泛型编程 -- 模板技术 template < class T > // 告诉编译器 下面代码吐过出现T, 不要报错, T是一个通用类型 // 等价于 template<typename T> void mySwap(T &a, T &b) { T tmp = a; a = b; b = tmp; } template<typename T> void mySwap2(T a, T b) {} void test() { int a = 10; int b = 20; mySwapInt(a, b); cout << "a = " << a << endl; // 20 cout << "b = " << b << endl; // 10 double c = 3.14; double d = 1.23; mySwapDouble(c, d); cout << "c = " << c << endl; // 1.23 cout << "d = " << d << endl; // 3.14 // 1. 自动类型推导, 必须要有参数类型才能推导 如 char e = 'e'; mySwap(a, e);这样就不行。 mySwap(a, b); mySwap(c, d); cout << "a = " << a << endl; // 10 cout << "b = " << b << endl; // 20 cout << "c = " << c << endl; // 3.14 cout << "d = " << d << endl; // 1.23 // 2. 显示指定类型, mySwap<int>(a, b); cout << "a = " << a << endl; // 20 cout << "b = " << b << endl; // 10 // 3. 模板必须要指定T才能使用 // mySwap2(); 这样会报错的,没有指定参数的类型 } int main() { test(); return EXIT_SUCCESS; }
#define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> #include<string> using namespace std; // 对 char和int数组进行用选择排序从大到小排序 template < class T > void Swap(T &a, T&b) { T temp = a; a = b; b = temp; } template<class T> void Sort(T arr[], int len) { for (int i = 0; i < len; i++) { int max = i; for (int j = i + 1; j < len; j++) { if (arr[max] < arr[j]) { // 交换下表 max = j; } } if (max != i) { // 交换数据 Swap(arr[max], arr[i]); } } } // 输出数组元素的模板 template < class T > void printArray(T arr[], int len) { for (int i = 0; i < len; i++) { cout << arr[i] << " "; } cout << endl; } void test() { char charArr[] = "helloworld"; int num = sizeof(charArr) / sizeof(char); Sort(charArr, num); printArray(charArr, num); int intArr[] = { 2, 5, 6, 3, 1, 9, 0, 8, 7, 4 }; num = sizeof(intArr) / sizeof(int); Sort(intArr, num); printArray(intArr, num); } int main() { test(); return EXIT_SUCCESS; }
/* 区别: 普通函数,可以进行隐式类型转换, 函数模板不可以 调用规则: 1. 如果出现重载,优先使用普通函数调用, 即使普通函数只有声明没有实现,只有声明会执行时报错的 2. 如果向强制执行模板,那么可以使用参数列表 3. 函数模板可以发生重载 4. 如果函数模板可以发生更好的匹配, 那么优先调用函数模板 */ #define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> #include<string> using namespace std; // 1. 普通函数与函数模板的区别 template<class T> void myPlus(T a, T b) { return a + b; } int myPlus2(int a, int b) { return a + b; } void test() { int a = 10; int b = 20; char c = 'c'; // myPlus(a, c); 类型推导不出来 myPlus2(a, c); // 可以执行, 普通函数,可以进行隐式类型转换 } // 2. 普通函数和函数模板的调用规则 template<class T> void myPrint(T a, T b) { cout << "模板调用的myPrint" << endl; }; void myPrint(int a, int b) { cout << "普通的函数调用myPrint" << endl; } template<class T> void myPrint(T a, T b, T c) { cout << "模板调用的myPrint发生重载" << endl; }; void myPrint2(); void test02() { int a = 10; int b = 20; // 1. 如果出现重载,优先使用普通函数调用, 即使普通函数只有声明没有实现,只有声明会执行时报错的 myPrint(a, b); // 普通的函数调用myPrint // 2. 如果向强制执行模板,那么可以使用参数列表 myPrint<>(a, b); // 模板调用的myPrint // 3. 函数模板可以发生重载 int c = 30; myPrint(a, b, c); // 模板调用的myPrint发生重载 // 4. 如果函数模板可以发生更好的匹配, 那么优先调用函数模板 char d = 'd'; char e = 'e'; myPrint(d, e); // 模板调用的myPrint,因为调用普通函数需要发生隐式数据类型转换 } int main() { test(); test02(); return EXIT_SUCCESS; }
/* 模板不能解决所有的数据类型,如果出现不能解决的数据类型,可以通过具体化来解决问题 语法 template<> 返回值 函数名<具体类型>(函数参数) */ #define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> #include<string> using namespace std; class Person { public: Person(string name, int age) { this->Name = name; this->Age = age; } string Name; int Age; }; template<class T> bool myCompare(T &a, T &b) { if (a == b) { return true; } return false; } // 通过具体化自定义数据类型, 解决自定义数据类型无法比较的问题 // 如果具体化能够优先匹配,那么就选择具体化 // 语法 template<> 返回值 函数名<具体类型>(函数参数) template<> bool myCompare<Person>(Person &a, Person &b) { if (a.Age == b.Age && a.Name == b.Name) { return true; } return false; } void test() { int a = 10; int b = 2; int ret = myCompare(a, b); cout << "ret = " << ret << endl; Person p1("Tom", 30); Person p2("Jerry", 15); ret = myCompare(p1, p2); // 直接这样是不行的,是会报错的, 需要具体化自定义数据类型解决问题 cout << "ret = " << ret << endl; } int main() { test(); return EXIT_SUCCESS; }
2.1 类模板的基本使用
/* 与函数模板区别,参数可以有默认类型 函数模板可以进行自动类型转换, 类模板不行 Person<string, int> p1("wangyong", 27); */ #define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> #include<string> using namespace std; // template<class NameType, class AgeType> // 没问题 template<class NameType, class AgeType = int> // 类模板可以有默认类型 class Person { public: Person(NameType name, AgeType age) { this->Name = name; this->Age = age; } void show() { cout << "姓名:" << this->Name << endl; cout << "年龄:" << this->Age << endl; } NameType Name; AgeType Age; }; void test() { // 自动类型推倒, 类内模板不支持 // Person p1("wangyong", 27); // 显示指定类型 Person<string, int> p1("wangyong", 27); p1.show(); } int main() { test(); return EXIT_SUCCESS; }
/* 成员函数一开始并不会创建出来,而是在运行时才会创建 */ #define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> #include<string> using namespace std; class Person1 { public: void showPerson1() { cout << "showPerson1" << endl; } }; class Person2 { public: void showPerson2() { cout << "showPerson2" << endl; } }; template<class T> class myClass { public: T obj; void func1() { obj.showPerson1(); } void func2() { obj.showPerson2(); } }; // 成员函数一开始并不会创建出来,而是在运行时才会创建 void test() { myClass<Person1> myclass; myclass.func1(); // showPerson1 } int main() { test(); return EXIT_SUCCESS; }
/* 1. 显示指定数据类型 2. 参数模板化 3. 整体模板化 // 如何查看类型 cout << typeid(T1).name() << endl; */ #define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> #include<string> using namespace std; template<class NameType, class AgeType = int> // 类模板可以有默认类型 class Person { public: Person(NameType name, AgeType age) { this->Name = name; this->Age = age; } void show() { cout << "姓名:" << this->Name << endl; cout << "年龄:" << this->Age << endl; } NameType Name; AgeType Age; }; // 1. 指定传入类型 void doWork(Person<string, int> &p) { p.show(); } // 2. 参数模板化 template<class T1, class T2> void doWork2(Person<T1, T2> &p) { // 如何查看类型 cout << typeid(T1).name() << endl; cout << typeid(T2).name() << endl; p.show(); } // 3. 整体模板化 template<class T> void doWOrk3(T &p) { cout << typeid(T).name() << endl; p.show(); } void test() { // 1. 指定传入类型 Person<string, int> p1 ( "wangyong", 27 ) ; doWork(p1); // 2. 参数模板化 Person<string, int> p2("wangyong", 27); doWork2(p2); // 3. 整体模板化 Person<string, int> p3("wangyong", 27); doWOrk3(p3); } int main() { test(); return EXIT_SUCCESS; }
/* 基类如果是模板类, 必须让子类告诉编译器 基类中的 T 是什么类型,如果不告诉的话,那么无法分配内存,编译不通过 继承模板的书写方式: class Child : public Base<int> */ #define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> #include<string> using namespace std; template<class T> class Base { public: T A; }; // Child 继承 Base, 必须要告诉 Base中的 T 的类型,都这 T 无法分配内存 class Child : public Base<int> { public: }; template <class T1, class T2> class Child2 :public Base<T2> { public: Child2() { cout << typeid(T1).name() << endl; // int cout << typeid(T2).name() << endl; // double } T1 B; }; void test() { Child2<int, double> child; //说明 T1是int类型, T2是double类型, T2是double类型,那么继承的Base的T就是double类型 } int main() { test(); return EXIT_SUCCESS; }
#define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> #include<string> using namespace std; template<class T1, class T2> class Person { public: Person(T1 name, T2 age); /*{ // 类内实现 this->Name = name; this->Age = age; }*/ void showPerson(); //{ // cout << "姓名: " << this - Name << endl; // cout << "年龄: " << this->Age << endl; //} T1 Name; T2 Age; }; // 类外实现成员函数 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 << "姓名: " << this -> Name << endl; cout << "年龄: " << this->Age << endl; } void test() { Person<string, int> p1("wangyong", 27); p1.showPerson(); } int main() { test(); return EXIT_SUCCESS; }
/* .p 和 .cpp 分别写声明和实现,但是由于类模板的成员函数运行阶段才去创建, 导致包含 .h 头文件,不会创建函数的实现,就无法解析外部命令。 解决方案: 1. #include "Person.cpp" 不推荐 2. 不要进行分文件编写, 写到同一个文件进行声明和实现,后缀名改为 .hpp。 将Person.cpp的代码直接写到 .h声明的文件中, 然后修改 .h为 .hpp,然后#include "Person.hpp" */ // .hpp #pragma once #define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> #include<string> using namespace std; template < class T1, class T2 > class Person { public: Person(T1 name, T2 age); void showPerson(); T1 Name; T2 Age; }; 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 << "姓名: " << this->Name << endl; cout << "年龄: " << this->Age << endl; } // main.cpp #include "Person.hpp" void test() { // 编译过程会出现问题的, 所以模板一般不建议分文件编写, // 解决方案:1. #include "Person.cpp" 2. 将Person.cpp的代码直接写到 .h声明的文件中, 然后修改 .h为 .hpp,然后#include "Person.hpp" Person<string, int> p1("wangyong", 27); p1.showPerson(); } int main() { test(); return EXIT_SUCCESS; }
2.7 类模板碰到友元函数
2.7.1 友元函数类内实现
#define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> #include<string> using namespace std; template<class T1, class T2> class Person { // 友元函数类内实现 friend void printPerson(Person<T1, T2> &p) { cout << "姓名: " << p.Name << endl; cout << "年龄: " << p.Age << endl; } public: Person(T1 name, T2 age) { this->Name = name; this->Age = age; } private: T1 Name; T2 Age; }; void test() { Person<string, int> p1("wangyong", 27); printPerson(p1); } int main() { test(); return EXIT_SUCCESS; }
/* friend void printPerson(Person<T1, T2> &p); // 普通函数声明 friend void printPerson<>(Person<T1, T2> &p); // 模板函数声明 需要让编译器看到函数并且看到这个Person类型提前声明 */ #define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> #include<string> using namespace std; //让编译器提前看到 printPerson 声明 template<class T1, class T2>class Person; template<class T1, class T2>void printPerson(Person<T1, T2> &p); template<class T1, class T2> class Person { // 友元函数类内实现 // friend void printPerson(Person<T1, T2> &p); // 这是普通函数的声明,但是实现是模板函数实现,所以直接这样写是报错的 friend void printPerson<>(Person<T1, T2> &p); // 这样代表模板函数声明 public: Person(T1 name, T2 age) { this->Name = name; this->Age = age; } private: T1 Name; T2 Age; }; // 友元函数类外实现 template<class T1, class T2> void printPerson(Person<T1, T2> &p) { cout << "姓名: " << p.Name << endl; cout << "年龄: " << p.Age << endl; } void test() { Person<string, int> p1("wangyong", 27); printPerson(p1); } int main() { test(); return EXIT_SUCCESS; }
2.8 类模板的应用--数组类的封装
#pragma once #define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> #include<string> using namespace std; template<class T> class MyArray { public: // 构造 explicit MyArray(int capacity) // 防止隐式类型转换 { this->Capacity = capacity; this->Size = 0; this->pAddress = new T[this->Capacity]; } MyArray(const MyArray & myArray) { this->Capacity = myArray.Capacity; this->Size = myArray.Size; this->pAddress = new T[this->Capacity]; for (int i = 0; i < this->Size; i++) { this->pAddress[i] = myArray[i]; } } ~MyArray() { if (this->pAddress != NULL) { delete[] this->pAddress; this->pAddress = NULL; } } // 赋值操作符重载 MyArray& operator=(MyArray & myArray) { // 先判断原始数据,有就清除 if (this->pAddress != NULL) { delete[] this->pAddress; this->pAddress = NULL: } this->Capacity = myArray.Capacity; this->Size = myArray.Size; this->pAddress = new T[this->Capacity]; for (int i = 0; i < this->Size; i++) { this->pAddress[i] = myArray[i]; } } // [] 重载 T& operator[](int index) { return this->pAddress[index]; } // 尾插法 void pushBack(T val) { this->pAddress[this->Size] = val; this->Size++; } // 获取大小 int getSIze() { return this->Size; } // 获取容量 int getCapacity() { return this->Capacity; } private: T * pAddress; int Capacity; int Size; };
#include "MyArray.hpp" class Person { public: Person(){}; Person(string name, int age) { this->Name = name; this->age = age; } string Name; int age; }; // 输出 int 类型的方法 void printInt(MyArray<int> &arr) { for (int i = 0; i < arr.getSIze(); i++) { cout << arr[i] << endl; } } // 输出 Person 类型的数组 void printPerson(MyArray<Person> &array) { for (int i = 0; i < array.getSIze(); i++) { cout << "姓名: " << array[i].Name << endl; cout << "年龄: " << array[i].age << endl; } } int main() { MyArray<int> arr(10); for (int i = 0; i < 10; i++) { arr.pushBack(i+100); } printInt(arr); Person p1("wang", 27); Person p2("yong", 28); Person p3("zhou", 26); MyArray<Person> arr2(10); arr2.pushBack(p1); arr2.pushBack(p2); arr2.pushBack(p3); printPerson(arr2); return EXIT_SUCCESS; }
浙公网安备 33010602011771号