模板(C++第三次实验)
1、模板函数(compare):
1.1 模板的概念
模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。
模板是创建泛型类或函数的蓝图或公式。库容器,比如迭代器和算法,都是泛型编程的例子,它们都使用了模板的概念。
每个容器都有一个单一的定义,比如 向量,我们可以定义许多不同类型的向量。
1.2 一般模板函数
模板函数的基本定义如下:
template<模板参数表>
类型名 函数名(参数表)
{
函数体
}
众所周知,有了“模子”后,用“模子”来批量制造陶瓷、塑料、金属制品等就变得容易了。程序设计语言中的模板就是用来批量生成功能和形式都几乎相同的代码的。有了模板,编译器就能在需要的时候,根据模板自动生成程序的代码。从同一个模板自动生成的代码,形式几乎是一样的。
这里有一个实例可以让我们更加清楚的了解模板函数的用法
代码:
#include <iostream>
using namespace std;
template<class T>
string compare(T a, T b) {
if (a > b)
return "a大";
else if (a < b)
return "b大";
else
return "一样大";
}
int main() {
int a = 1;
int b = 3;
double a1 = -4.0;
double b1 = -9.9;
string a2 = "1";
string b2 = "1";
cout << compare(a, b) << endl;
cout << compare(a1, b1) << endl;
cout << compare(a2, b2);
return 0;
}
运行结果为:
从代码可以看出只要有了一个模板就不用多次创建传入参数是double、string等类型的compare函数。在程序编译过程中节省了大量的时间。
1.3 特化模板函数
什么是特化模板函数呢?
对于一些特殊的类型,使用针对这个类型写的特化的模板,与泛型的模板相比,可能效率更高。
像上面所说的一般模板函数是无法处理char字符串的比较的,那么此时就需要特化模板来处理。
特化模板函数的基本定义如下:
template <>
函数名<特化类型>(特化类型 参数1, 特化类型 参数2 , …)
{
函数体
}
这里举一个例子,若用一般模板函数来进行字符串的比较,就会出现以下结果:
代码:
#include<stdio.h>
#include<iostream>
using namespace std;
template <class T>
int compare(const T& v1, const T& v2)
{
if (v1 < v2) return -1;
else if (v1 > v2) return 1;
else return 0;
}
int main() {
const char* a = "qqz";
const char* b = "qqq";
cout << compare(a, b) << endl;
}
运行结果:
正常来说,v1应当是要比v2大的,但是经过模板函数处理后,生成的结果为-1。
这是因为在main函数处理时传入的参数为char*(指针类型),模板函数比较的是指针地址的大小而并非该地址所存的数据的大小。此时就要引入特化的概念,可以简单的理解为,特殊的情况用特殊的方法处理。
代码:
#include<stdio.h>
#include<iostream>
using namespace std;
template <class T>
int compare(const T& v1, const T& v2)
{
if (v1 < v2) return -1;
else if (v1 > v2) return 1;
else return 0;
}
template < >
int compare<const char*>(const char* const& v1, const char* const& v2)
{
return strcmp(v1, v2);
}
int main() {
const char* a = "qqq";
const char* b = "qqz";
cout << compare(a, b) << endl;
}
运行结果:
经过特化模板函数的处理,成功输出正确结果1。
2、模板类Queue或Stack
概念
人们需要编写多个形式和功能都相似的函数,因此有了函数模板来减少重复劳动;人们也需要编写多个形式和功能都相似的类,于是 C++ 引人了类模板的概念,编译器从类模板可以自动生成多个类,避免了程序员的重复劳动。
类模板的基本定义
template<模板参数表>
class 类名
{
类成员变量
}
2.1 模板类(Queue,Stack)
queue.h
#ifndef QUEUE_H
#define QUEUE_H
#include <iostream>
using namespace std;
//定义类模板
template<class Type> class Queue;
template<class Type> class QueueItem {
QueueItem(const Type& t) : item(t), next(0) {}
Type item;
QueueItem* next;
friend class Queue<Type>;
//输出运算符的重载
friend ostream& operator<<(ostream& os, const Queue<Type>& q);
QueueItem<Type>* operator++() {
return next;
}
Type& operator*() {
return item;
}
};
template<class Type> class Queue {
public:
Queue() : head(0), tail(0) {}
Queue(const Queue& q) : head(0), tail(0) {
copy_items(q);
}
//成员函数模板
template <class It>
Queue(It beg, It end) : head(0), tail(0) { copy_items(beg, end); }
template<class It> void assign(It beg, It end);
Queue& operator=(const Queue&);
//析构函数
~Queue() { destroy(); }
Type& front() { return head->item; }
const Type& front() const { return head->item; }
//元素入队
void push(const Type&);
//队尾元素出队
void pop();
bool empty() const { return head == 0; }
//输出符重载(第二个参数表示待输出的队列)
//(全局)友元函数
friend ostream& operator<<(ostream& os, const Queue<Type>& q) {
os << "< ";
QueueItem<Type>* p;
for (p = q.head; p; p = p->next) {
os << p->item << " ";
}
os << ">";
return os;
}
const QueueItem<Type>* Head() const { return head; }
const QueueItem<Type>* End() const { return (tail == NULL) ? NULL : tail->next; }
private:
QueueItem<Type>* head;
QueueItem<Type>* tail;
void destroy();
void copy_items(const Queue&);
template<class It>
void copy_items(It beg, It end);
};
template<class Type>
void Queue<Type>::pop() {
QueueItem<Type>* p = head;
head = head->next;
delete p;
}
template<class Type>
void Queue<Type>::push(const Type& val) {
QueueItem<Type>* pt = new QueueItem<Type>(val);
if (empty()) {
head = tail = pt;
}
else {
tail->next = pt;
//元素添加到队列后tail指针指向该元素
tail = pt;
}
}
template < >
void Queue<const char*>::push(const char* const& val);
template < >
void Queue<const char*>::pop();
template <class Type>
void Queue<Type>::copy_items(const Queue& orig) {
for (QueueItem<Type>* pt = orig.head; pt; pt = pt->next) {
push(pt->item);
}
}
template <class Type>
Queue<Type>& Queue<Type>::operator=(const Queue& q)
{
destroy();
copy_items(q);
}
template<class Type> template<class It> void Queue<Type>::assign(It beg, It end)
{
destroy();
copy_items(beg, end);
}
template<class Type> template<class It> void Queue<Type>::copy_items(It beg, It end)
{
while (beg != end) {
push(*beg);
++beg;
}
}
//销毁队列
template<class Type>
void Queue<Type>::destroy()
{
while (!empty()) {
pop();
}
}
void TestQueue();
#endif#pragma once
queue.cpp
#include "queue.h"
#include<cstring>
void TestQueue()
{
Queue<int> qt;
int d = 5;
qt.push(1);
qt.push(d);
qt.push(999);
cout << qt;
}
main.cpp
#include<iostream>
#include "queue.h"
using namespace std;
int main() {
TestQueue();
return 0;
}
运行结果:
2.2 成员模板函数
在上述queue.h中有一段成员模板函数的代码
//成员函数模板
template <class It>
Queue(It beg, It end) : head(0), tail(0) { copy_items(beg, end); }
将测试函数中的内容改成
#include "queue.h"
#include<cstring>
void TestQueue()
{
int a[6] = { 0,1,2,3,4,5 };
Queue<int> q(a, a + 6);
cout << q;
}
运行结果为:
2.3 模板特化:模板函数特化、模板成员函数特化、模板类特化
2.3.1成员函数模板特化
queue.cpp中的内容改为
#include "queue.h"
#include<cstring>
template <>
void Queue<const char*>::push(const char* const& val) {
char* new_item = new char[strlen(val) + 1];
//将val的值赋值到new_item,其中strlen(val)为需要复制的长度
strncpy_s(new_item, strlen(new_item), val, strlen(val));
QueueItem<const char*>* pt = new QueueItem<const char*>(new_item);
if (empty()) {
head = tail = pt;
}
else {
tail->next = pt;
tail = pt;
}
}
template <>
void Queue<const char*>::pop() {
QueueItem<const char*>* p = head;
delete head->item;
head = head->next;
delete p;
}
void TestQueue()
{
Queue<const char*> q1;
q1.push("i ");
q1.push("love");
q1.push("u");
cout << q1;
}
其余的和2.1中的代码一样
运行结果:
3、模板类AutoPtr
首先添加一个AutoPtr类
#ifndef AUTOPTR_H
#define AUTOPTR_H
template <class T>
class AutoPtr
{
public:
//构造函数
AutoPtr(T* pData);
//拷贝构造函数
AutoPtr(const AutoPtr<T>& h);
//声明周期结束时调用析构函数
~AutoPtr();
//等号的重载
AutoPtr<T>& operator=(const AutoPtr<T>& h);
//用户数减1
void decrUser();
//指针运算符重载(返回的指针允许被改变)
T* operator ->() {
return m_pData;
}
//返回一个对象(能使用成员运算符(".")来访问成员变量)
T& operator*() {
return *m_pData;
}
const T& operator *() const {
return *m_pData;
}
//指针运算符重载(返回的指针不允许被改变)
const T* operator -> () const {
return m_pData;
}
private:
//存储数据
T* m_pData;
//存储用户数
int* m_nUser;
};
template < class T>
AutoPtr<T>::AutoPtr(T* pData)
{
m_pData = pData;
//初始化用户数为1
m_nUser = new int(1);
}
template < class T>
AutoPtr<T>::AutoPtr(const AutoPtr<T>& h) {
m_pData = h.m_pData;
m_nUser = h.m_nUser;
//用户数加1
(*m_nUser)++;
}
template < class T>
AutoPtr<T>& AutoPtr<T>::operator=(const AutoPtr<T>& h)
{
decrUser();
m_pData = h.m_pData;
m_nUser = h.m_nUser;
(*m_nUser)++;
}
template < class T>
void AutoPtr<T>::decrUser()
{
--(*m_nUser);
if ((*m_nUser) == 0) {
//删除数据
delete m_pData;
//地址赋为空
m_pData = 0;
delete m_nUser;
m_nUser = 0;
}
}
template < class T>
AutoPtr<T>::~AutoPtr()
{
decrUser();
}
#endif // AUTOPTR_H
构造函数
template < class T>
AutoPtr<T>::AutoPtr(T* pData)
{
m_pData = pData;
//初始化用户数为1
m_nUser = new int(1);
}
析构函数
template < class T>
AutoPtr<T>::AutoPtr(const AutoPtr<T>& h) {
m_pData = h.m_pData;
m_nUser = h.m_nUser;
//用户数加1
(*m_nUser)++;
}
等号重载
template < class T>
AutoPtr<T>& AutoPtr<T>::operator=(const AutoPtr<T>& h)
{
decrUser();
m_pData = h.m_pData;
m_nUser = h.m_nUser;
(*m_nUser)++;
}
指向运算符重载
template < class T>
void AutoPtr<T>::decrUser()
{
--(*m_nUser);
if ((*m_nUser) == 0) {
//删除数据
delete m_pData;
//地址赋为空
m_pData = 0;
delete m_nUser;
m_nUser = 0;
}
}