一、什么模板
模板的引入跟泛型编程 有关,泛型编程指编写和编译时,对于参数的类型是一个不确定的类型,直到程序运行时,才能确定真正的类型。而泛型编程的实现主要通过函数模板和类模板。
二、模板有几种
模板有两种,函数模板和类模板。
函数模板的语法:
cpp语法:以关键字template指定一个函数为函数模板 在定义函数时,要指定泛型,指定泛型通常使用关键字 typename 或者 class 泛型通常用大写字母T代表 泛型符号:<> template<typename T> 函数的返回值 函数名(参数列表) { 函数体 } 备注:一个函数如果它是函数模板,不是所有的类型都是使用泛型。
类模板的语法
cpp// 类中成员变量类型和成员函数的参数的烈性不确定,在类创建对象时,才确定其类型 // 语法示例 template<class T> class 类名 { // 类的定义 private: T 成员变量1; public: 类名(T a = T()); // 构造函数 }
三、模板的特性
1、函数模板
(1)函数模板在调用时,可以根据实参自动推导泛型,也可以显式指定泛型
(2)当普通函数和函数模板同时出现,如果普通函数有严格匹配,那么,优先调用普通函数
(3)函数模板也可以进行函数重载,并且泛型也可以设置默认参数。
(4)**函数模板经过实例化后生成的具体函数称为模板函数。**模板函数的编译机制是二次编译,对于模板函数而言,它真正的函数名是发生变化的,新函数名 = 原函数名 + <具体的类型>。
2、类模板
类中成员变量类型和成员函数的参数类型不确定,在类创建对象时,才确定其类型
四、使用场景
1、类模板的应用,可以用来设计容器 ,也可以用来在设计模式中。
2、模板类也是一个类,它也可以用来做基类。
五、案例
1、函数模板的简单使用
cpp#include <iostream> #include <cstring> using namespace std; // 模板的声明必须要和函数的声明紧挨一起 template <typename T> void show(T *a, int len) { for(int i = 0; i < len; i++) { cout << a[i] << " "; } cout << endl; } int main() { int a[5]; for(int i = 0; i < 5; i++) { a[i] = i+1; } show(a, 5); show("hello", strlen("hello") + 1); return 0; }
2、类模板的简单使用
cpp#include <iostream> #include <cstring> using namespace std; template <class T> class Test { private: T *array; int len; public: Test(const T* array = T(), int len = 1) { if(len <= 0) { cout << "len is less than 0" << endl; return; } this->array = new T[len]; this->len = len; for(int i = 0; i < len; i++) { this->array[i] = array[i]; } } ~Test() { if(array != nullptr) { delete [] array; array = nullptr; } } void show() { if(array == nullptr || len == 0) { cout << "array is null" << endl; return; } for(int i = 0; i < len; i++) { cout << array[i] << " "; } cout << endl; } }; int main() { int a[5] = {1, 2, 3, 4, 5}; Test<int> test1(a, 5); Test<char> test2((char*)"hello", strlen("hello") + 1); test1.show(); test2.show(); return 0; }
3、基类为模板类,派生类为普通类
cpp#include <iostream> using namespace std; // 模板类 template <class T> class Base // 类名为 Base<T> { protected: T a; public: Base(T a1 = T()) { this->a = a1; } }; // 派生类为普通类,此时模板类要指定具体数据类型 class Test:public Base<int> { private: int b; public: // 必须显式调用构造函数初始化列表来初始化基类成员 Test(int a = 0, int b = 0):Base<int>(a) { this->b = b; } // 重载输出流运算符函数 friend ostream& operator << (ostream & output, const Test& other); }; ostream& operator << (ostream & output, const Test& other) { output << "a = " << other.a << endl; output << "b = " << other.b << endl; return output; } int main() { Test test1(10, 100); cout << test1 << endl; return 0; }
4、基类为模板类,派生类也为模板类
cpp#include <iostream> using namespace std; // 模板类 template <class T> class Base // 类名为 Base<T> { protected: T a; public: Base(T a1 = T()) { this->a = a1; } }; // 派生类也为模板类 template <class T> class Test:public Base<T> { private: T b; public: // 必须显式调用构造函数初始化列表来初始化基类成员 Test(T a = 0, T b = 0):Base<T>(a) { this->b = b; } void show() { cout << "a = " << this->a << endl; cout << "b = " << this->b << endl; } }; int main() { Test<int> test1(100, 1000); // 必须显式声明数据类型 Test<char*> test2((char*)"hello", (char*)"world"); test1.show(); test2.show(); return 0; }
5、C++设计模式的单例模式
单实例模型是指保证系统中,只有一个类对象,也就是说,在设计一个类的时候,该类的对象只能在一个系统中,保证只有一(该类不能在类的外部,通过构造函数来创建对象)比如:资源的管理者,老师等。
cpp#include <iostream> using namespace std; // 第一步:设计一个单例模式 --- 模板类 // 单例模式的目的就是为了得到泛型类型(T)对象的唯一,那么,就在泛型类型(T)类中创建对象的方式全部去禁止掉 // 只通过模板类的静态成员函数这个接口来获取泛型这个对象 // 该单例模式存在线程安全问题(已修改),对于CPU是一个多线程,当其中一个线程正在执行创建对象时, // 但是这个对象还没有创建成功,当另外一个线程想要创建对象,发现对象还有,那么,它也会去创建这个对象 // 为了解决这个问题,那么就给互斥资源上锁。 bool mutex = true; // 互斥变量 template<class T> class SingleTon { private: static T *instance; public: static T* getInstance() { if(instance == nullptr) { while(1) { if(mutex) { mutex = false; // 上锁 instance = new T; // 相当于调用了无参的构造函数 mutex = true; // 解锁 break; // 退出 } } } return instance; } }; // 第二步:设计要实例化的类 class Admin { public: void showInfo() { cout << "name:\t" << name << endl; cout << "age:\t" << age << endl; } private: string name; int age; // 把所有可以创建类对象的成员函数禁止访问,留下一个构造函数来创建唯一的对象 // 而且要把构造函数进行私有化,通过类SingleTon的静态指针来调用 Admin(const string &name = "admin", int age = 20) { this->name = name; this->age = age; } // 禁止使用拷贝构造函数 Admin(const Admin& other) = delete; Admin& operator = (const Admin& other) = delete; friend class SingleTon<Admin>; // 声明为友元类 }; template<class T> T *SingleTon<T>::instance = nullptr; int main() { // 第三步,通过单例模式静态成员函数来获取唯一的对象 Admin *p1 = SingleTon<Admin>::getInstance(); Admin *p2 = SingleTon<Admin>::getInstance(); // 不管调用多少次,p1, p2, ..., pn值都是一样的 cout << "p1 = " << p1 << endl; cout << "p2 = " << p2 << endl; p1->showInfo(); p2->showInfo(); return 0; }
六、总结
模板的引入是为了适应泛型编程,简单来讲就是编写一份功能相同的代码,只是操作的数据类型不同,此时采用泛型编程可以很好解决问题,C++中采用模板来实现。本篇还讲解了设计模式中的单例模式,更多的应用场景看另外一篇《重学C++系列之智能指针》。