C++模板
龟龟不断向前 人气:01.内容引入
不知道大家是否在高中时背过英语范文模板,以下就是博主的回忆:
这篇模板是一些英语比较好的老师写的。
每当碰到感谢信时,我都会狂喜,尽管感谢的内容不同,地点不同,我都可以去根据模板,再根据作文分析模板的那些空对应应该填入什么。
其实呢c++中也用模板,但是这个时候,我们是写模板的人,而编译器变成了那个根据模板照葫芦画瓢的人
2.模板函数
C语言写交换函数
#include<iostream> using namespace std; void Swapi(int* a, int* b) { int tmp = *a; *a = *b; *b = tmp; } void Swapd(double* a, double* b) { double tmp = *a; *a = *b; *b = tmp; } //…… int main() { int a = 1, b = 2; Swapi(&a, &b); double c = 1.1, d = 2.2; Swapd(&c, &d); return 0; }
要实现不同类型的交换,实参不仅要传地址,而且不同类型的函数的名字要保持不同
至于为什么会这样,大家可以去看看我的文章。解释了为什么c语言不支持函数重载:
C++写交换函数
#include<iostream> using namespace std; void Swap(int& x, int& y) { int tmp = x; x = y; y = tmp; } void Swap(double& x, double& y) { double tmp = x; x = y; y = tmp; } //…… int main() { int a = 1, b = 2; Swap(a, b); double c = 1.1, d = 2.2; Swap(c, d); return 0; }
C++在语法上增加了引用和函数重载,在一定程度上弥补了c语言的不足,但是上述代码明明逻辑很相似,却还是要我们去实现不同类型的代码,对于我们这种懒人来说,简直就是煎熬
但是计算机他是一个任劳任怨的好铁,不来不会感到疲劳,厌倦,是一个头脑优点笨笨的但是计算能力超强的大铁块。
模板交换函数的语法及其原理
语法
#include<iostream> using namespace std; template <class T> void Swap(T& x, T& y) { T tmp = x; x = y; y = tmp; } int main() { int a = 1, b = 2; Swap(a, b); double c = 1.1, d = 2.2; Swap(c, d); return 0; }
这样写交换函数是不是就轻松多了,但是我们思考以下,上述代码调用的是一个Swap函数还是两个Swap函数呢?
回顾我们说的模板,是我们写的模板,然后编译器照着模板帮我们写出了int
和double
类型的交换函数。
原理
图解:
我们也可以通过调试上述代码,转到反汇编,看看调用的函数是否真的是不同的函数。
理解显示实例化和隐式实例化
我们那模板加法函数来理解
#include<iostream> using namespace std; T Add(const T& x,const T& y) { return x + y; } int main() { int a = 1, b = 2; double c = 1.1, d = 2.2; cout << Add(a, b) << endl;//编译器要自己推类型的是隐式实例化 cout << Add(c, d) << endl; //cout << Add(a, c) << endl;//error这样的写法就错了,为难编译器了,编译器也推不出来了 cout << Add<int>(a, c) << endl;//不需要编译器去推的是显示实例化 cout << Add<double>(b, d) << endl; cout << Add(a, (int)c) << endl; return 0; }
编译器要自己去推T是什么类型的,就是隐式实例化
而由我们告诉编译器T是什么类型的,就是显示实例化
关于编译器也是懒人这件事
我们来看几道模板函数的代码来看看编译器是如何做事的:
#include<iostream> using namespace std; int Add(int left, int right) { return left + right; } // 通用加法函数 template<class T> T Add(T left, T right) { return left + right; } int main() { Add(1, 2); // 与非模板函数匹配,编译器不需要特化 Add<int>(1, 2); // 调用编译器特化的Add版本 return 0; }
如果调试了上述代码就会发现,编译器第一次调用的是第一个Add函数,第二次由于我们的指定,编译器调用的是模板加法函数。
#include<iostream> using namespace std; int Add(int left, int right) { return left + right; } template < class T1, class T2> T1 Add(const T1 x,const T2 y) { return x + y; } int main() { Add(1, 2); Add(1, 2.0);//如果不写模板,会进行一个类型转换,再去调用第一个 return 0; }
3.类模板
由于c++的顺序表是用vector表示的,下面咱们的类名也用vector表示
像以前我们实现一个顺序表是这样的。
typedef int VDateType; class vector { public: //…… private: VDateType* _a; size_t _size; size_t _capacity; }; int main() { vector v1; vector v2; return 0; }
但是我们无法让v1
是int类型的顺序表,v2
是double类型的顺序表。
用模板类来实现
#include<iostream> #include<assert.h> using namespace std; namespace kcc { template<class T> class vector { public: vector() :_a(nullptr) , _size(0) , _capacity(0) {} // 拷贝构造和operator= 这里涉及深浅拷贝问题,还挺复杂,后面具体再讲 ~vector() { delete[] _a; _a = nullptr; _size = _capacity = 0; } void push_back(const T& x) { if (_size == _capacity) { int newcapacity = _capacity == 0 ? 4 : _capacity * 2; T* tmp = new T[newcapacity]; if (_a) { memcpy(tmp, _a, sizeof(T) * _size); delete[] _a; } _a = tmp; _capacity = newcapacity; } _a[_size] = x; ++_size; } // 读+写 T& operator[](size_t pos); size_t size(); private: T* _a; size_t _size; size_t _capacity; }; // 模板不支持分离编译,也就是声明在.h ,定义在.cpp,原因后面再讲 // 建议就是定义在一个文件 xxx.h xxx.hpp // 在类外面定义 template<class T> T& vector<T>::operator[](size_t pos) { assert(pos < _size); return _a[pos]; } template<class T> size_t vector<T>::size() { return _size; } } int main() { kcc::vector<int> v1; // int v1.push_back(1); v1.push_back(2); v1.push_back(3); v1.push_back(4); // v1.operator[](3); //cout << v1[3] << endl; //cout << v1[5] << endl; for (size_t i = 0; i < v1.size(); ++i) { v1[i] *= 2; } cout << endl; for (size_t i = 0; i < v1.size(); ++i) { cout << v1[i] << " "; } cout << endl; kcc::vector<double> v2; // double v2.push_back(1.1); v2.push_back(2.2); v2.push_back(3.3); v2.push_back(4.4); for (size_t i = 0; i < v2.size(); ++i) { cout << v2[i] << " "; } cout << endl; return 0; }
如果内部成员函数在类的外面定义的话,要加上类名::
当然了,本文章并不是重点介绍顺序表vector的实现,而是让大家看看类模板的效果
vector会在后续的文章中更新,敬请期待!
加载全部内容