- 实验环境
- 模板概念
- 特化模板函数
- 模板类
- 队列类
*** 作系统:win10
gcc:8.1.0
开发软件:qt5.14.2
在C++的相关代码开发中,我们常常会遇到的是关于代码通用性的问题;模板就是C++支持参数化程序设计的工具,通过它可以实现参数化多态性。这里可以看一下两个例子。
int sum(int x,int y) { return x+y; } double sum(double x,double y) { return x+y; }
事实上两个函数除了数据类型以外进行的功能是一样的,但如果像这样分开写其实大大的降低了代码整体的通用性,对于设计和使用都是不方便的选择。这就是模板需要起作用的地方,模板的定义如下
template<模板参数表> 类型名 函数名(参数表) { 函数 }
为了方便理解,以上述的例子为出发点进行衍生。
#includeusing namespace std; template test sum(test a,test b) { return a+b; } int main() { int test1=1,test2=2; double test3=3,test4=4; cout< 特化模板函数
可以看到,这样的方式成功完成了对一般模板函数的成功运用。但此时需要考虑一个特殊状况,使用模板时会遇到一些特殊的类型需要特殊处理,不能直接使用当前的模板函数,所以此时我们就需要对该类型特化出一个模板函数,如下所示。
templatebool Isequal(test &t1, test &t2) { return t1 == t2; } 上述的函数可以判断传入的两个参数是否相等,但显而易见的,当数据类型为字符型时,上述函数无法生效,故在此使用特化的模板函数。
template<> bool Isequal(char*& t1, char*& t2) { return strcmp(t1, t2) == 0; } 上面展示的就是特化的模板函数,需要注意几点
模板类
- 使用模板特化时,必须要先有基础的模板函数(就是上面第一个模板函数)
- template 后直接跟<> 里面不用写类型
- 函数名<特化类型>(特化类型 参数1, 特化类型 参数2 , …) 在函数名后跟<>其中写要特化的类型
- 特化的函数的函数名,参数列表要和原基础的模板函数相同
同样 我们简单的运行来验证结果。
从上图可以看到,运行的结果符合预期,特化的函数成功运作了。既然存在模板函数的概念,同样也存在模板类的概念。使用类模板的过程可以使得程序员为类定义一种模式,使得类中的部分数据成员,部分成员函数的参数,返回值和局部变量能取不同的类型。这里为了方便说明,简单的建立一个**队列(queue)**的类来进行实践。
首先让我们对简单的模板类进行说明。
templateclass Complex{ public: //构造函数 Complex(T a, T b) { this->a = a; this->b = b; } //运算符重载 Complex operator+(Complex &c) { Complex tmp(this->a+c.a, this->b+c.b); return tmp; } private: T a; T b; } int main() { //对象的定义,必须声明模板类型,因为要分配内容 Complex a(10,20); Complex b(20,30); Complex c = a + b; return 0; } 其中简单的展示了类模板的使用方式,重要的部分都有进行注释。
队列类话不多说,先把实现的代码放在下面
#includeusing namespace std; template struct Node { T data; Node *next; }; template class MyQueue { public: MyQueue(); // 构造函数 ~MyQueue(); // 析构函数 void Push(const T& x); // 压入队列 T Front(); // 获取队头元素 T Back(); // 获取队尾元素 void Pop(); // 删除队头元素 bool Empty() const; // 判断队列是否为空 int Size() const; // 返回队列的大小 private: Node *head; // 头指针 Node *tail; // 尾指针 }; // 构造函数 template MyQueue ::MyQueue() { head = tail = NULL; } // 析构函数 template MyQueue ::~MyQueue() { Node *current; while(head != NULL) { current = head; head = head->next; delete current; current = NULL; } } // 压入队列 template void MyQueue ::Push(const T &x) { Node *current; current = new Node (); if(current == NULL) { std::cerr << "申请空间失败!n"; exit(1); } current->data = x; current->next = NULL; if(head == NULL) head = current; else tail->next = current; tail = current; } // 获取队头元素 template T MyQueue ::Front() { Node *current; if(head == NULL) { std::cout << "队列为空!n"; exit(1); } current = head; return current->data; } // 获取队尾元素 template T MyQueue ::Back() { Node *current; if(head == NULL) { std::cout << "队列为空!n"; exit(1); } current = tail; return tail->data; } // 删除队头元素 template void MyQueue ::Pop() { if(head == NULL) { std::cout << "队列为空!n"; exit(1); } head = head->next; } // 判断队列是否为空 template bool MyQueue ::Empty() const { if(head == NULL) return true; return false; } // 返回队列的大小 template int MyQueue ::Size() const { int n; Node *current; n = 0; current = head; while(current != NULL) { n++; current = current->next; } return n; } 可以看到,上面的类在使用了模板类的前提下实现了队列的简单的构造函数,析构函数,向队列中加入元素,删除队列元素,返回队列长度,获取队头和队尾的长度等的基本 *** 作。
先让我们看一下实现的效果
与模板类的实现在上述的队列代码中都有体现,以下任然需要补充一些在使用类模板时会遇到的问题。
- 模板类本身未指定所使用的数据类型,不能单独编译模板类的实现。 只用在使用模板类的阶段,指定了模板中的数据类型,编译器才能正常编译。因此,在实际开发中,必须把实现全部写在头文件里面,把声明和实现分开的做法不可取。
- 模版不支持在局部函数中声明定义或使用
- 自动类型推导,必须推导出一致的数据类型T,才可以使用模板必须要确定出T的数据类型,才可以使用。
- 模版类的定义和实现不能分开写在不同文件中,否则会导致编译错误
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)