- 一、模板函数
- 1.1 一般模板函数
- 1.2 特化模板函数
- 二、模板类Queue或Stack
- 2.1 成员模板函数
- 2.2 模板特化
- 三、模板类AutoPtr
在此之前,先说明一下模板的概念:模板就是建立通用的模具,大大提高复用性。需要注意的是:模板不可以直接使用,它只是一个框架。
而对函数模板来说,它的作用是建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。具体语法如下:
template函数声明或定义
template — 声明创建模板
typename — 表面其后面的符号是一种数据类型,可以用class代替
T — 通用的数据类型,名称可以替换,通常为大写字母
现在用一个案例来进行演示。
#include#include using namespace std; template void mySwap(T& a, T& b) { T temp = a; a = b; b = temp; } void test01() { int a = 10; int b = 20; mySwap (a, b); cout << "a = " << a << endl; cout << "b = " << b << endl; } int main() { test01(); return 0; }
可以看到,交换成功
对于不同的数据类型,也可以进行交换:
另外,使用函数模板时,如果用自动类型推导,不会发生自动类型转换,即隐式类型转换。
如创建加法函数
templateT myAdd(T a, T b) { return a + b; }
报错的原因是使用自动类型推导时,不会发生隐式类型转换
而加上int,用显示指定类型,可以发生隐式类型转换
1.2 特化模板函数先创建一个比较函数
templatebool myCompare(T a, T b) { if (a != b) return false; else return true; }
运行不报错。
但是,能比较类中的成员函数吗?
class Person {
public:
string m_Name;
Person(string name) {
this -> m_Name = name;
}
};
生成失败,显然是不能这样使用的。
这时候,我们就引入特化模板函数概念。
模板特化:就是在实例化模板时,对特定类型的实参进行特殊处理,即实例化一个特殊的实例版本,当以特化定义时的形参使用模板时,将调用特化版本。简而言之,就是指定了模板的使用数据类型。
另外,它的优先级高于函数模板。
template<> bool myCompare(Person a,Person b) {
if (a.m_Name == b.m_Name) {
return true;
}
else return false;
}
注意:特化模板类型必须与原模板函数一样。如果原来的一般模板函数类型,如:
templatevoid mySwap(T& a, T& b)
那么在特化时,原来里面带&,特化函数也必须带,否则报错。
下面以实现Queue为案例
2.1 成员模板函数成员模板函数,就是类模板实例化出的对象,向函数传参。
一共有三种传入方式:
- 指定传入的类型 — 直接显示对象的数据类型
- 参数模板化 — 将对象中的参数变为模板进行传递
- 整个类模板化 — 将这个对象类型 模板化进行传递
在类QueueItem中,首先声明一个模板template class Type ,在这里是将整个类都模板化,其他函数都是为了这个类型的参数做工作。这很好理解:在我们创建队列Queue的时候,就是对一个数据类型进行入队出队,显而易见,里面的操作函数都是在为这一个数据类型做服务的。
实际上,模板的本质区别就是改变替换参数类型,实现过程也很简单。我的理解是:将固定的变量类型,如int, double…用模板里的全部代替。在调用的时候再把要用的参数名传进。
以其中一个QueueItem例为例
template2.2 模板特化class Queue; template class QueueItem { QueueItem(const Type& t) :item(t), next(0) {} Type item; QueueItem* next; friend class Queue ; friend ostream& operator<<(ostream& os, const Queue & q); public: QueueItem * operator++() { return next; } Type& operator*() { return item; } };
以Queue类为例。
首先介绍模板特化的几种类型:模板函数特化、模板成员函数特化、模板类特化。
在做特化时,同创建一般函数模板一样,都需要在类前做一个模板声明并指定名称,如:template
,随后在使用过程中,与模板函数一样,将之前写过的Type 参数名改成你想要的,如int,double…就好。
templateclass Queue { public: Queue() :head(0), tail(0) {} Queue(const Queue& q) :head(0), tail(0) { copy_items(q); } template Queue(It beg, It end) : head(0), tail(0) { copy_items(beg, end); } template 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 & q) { os << "< "; QueueItem * p; for (p = q.head; p; p = p‐ > next) { os << p‐ > item << " "; } os << ">"; return os; } const QueueItem * Head() const { return head; } const QueueItem * End() const { return (tail == NULL) ? NULL : tail‐ > next; } private: QueueItem * head; QueueItem * tail; void destroy(); void copy_items(const Queue&); template void copy_items(It beg, It end); };
在做特化时,将实现对应的位置改成想要使用的。
template <> void Queue三、模板类AutoPtr::Push( const char * const & str) { char * pVal = new char[strlen(str)+1]; strcpy(pVal,str); QueItem * p = new QueItem (pVal); if(isEmpty()) { head = tail = p; } else { tail‐>next = p; tail = p; } } template<> void Queue ::Pop() { if(isEmpty()) { return; } QueItem * p = head; head = head‐>next; delete []p‐>item; delete p; } template <> int compare(const char* const a, const char* const b) { return strcmp(a,b); }
在学会了模板的几种使用方法后,下面将进行实战。
首先说一下什么是AutoPtr。智能指针是一个类,这个类的构造函数中传入一个普通指针,析构函数中释放传入的指针。智能指针的类都是栈上的对象,所以当函数(或程序)结束时会自动被释放,于是我们用个简单的案例来实现以下。
#include#include using namespace std; template class Shared_ptr { private: size_t* m_count; T* m_ptr; public: //构造函数 Shared_ptr() : m_ptr(nullptr), m_count(new size_t) {} Shared_ptr(T* ptr) : m_ptr(ptr), m_count(new size_t) { cout << "空间申请:" << ptr << endl; *m_count = 1; } //析构函数 ~Shared_ptr() { --(*m_count); if (*m_count == 0) { cout << "空间释放:" << m_ptr << endl; delete m_ptr; delete m_count; m_ptr = nullptr; m_count = nullptr; } } //拷贝构造函数 Shared_ptr(const Shared_ptr& ptr) { m_count = ptr.m_count; m_ptr = ptr.m_ptr; ++(*m_count); } //拷贝赋值运算符 void operator=(const Shared_ptr& ptr) { Shared_ptr(std::move(ptr)); } //移动构造函数 Shared_ptr(Shared_ptr&& ptr) : m_ptr(ptr.m_ptr), m_count(ptr.m_count) { ++(*m_count); } //移动赋值运算符 void operator=(Shared_ptr&& ptr) { Shared_ptr(std::move(ptr)); } //解引用运算符 T& operator*() { return *m_ptr; } //箭头运算符 T* operator->() { return m_ptr; } //重载布尔值操作 operator bool() { return m_ptr == nullptr; } T* get() { return m_ptr; } size_t use_count() { return *m_count; } bool unique() { return *m_count == 1; } void swap(Shared_ptr& ptr) { std::swap(*this, ptr); } }; int main() { Shared_ptr p1(new int); *p1 = 222; cout << "值:" << *p1 << " 引用计数:" << p1.use_count() << endl; { Shared_ptr p2(p1); *p2 = 333; cout << "值:" << *p2 << " 引用计数:" << p1.use_count() << endl; Shared_ptr p3(p2); *p3 = 444; cout << "值:" << *p3 << " 引用计数:" << p1.use_count() << endl; } cout << "引用计数:" << p1.use_count() << endl; Shared_ptr q1(new string("我是string1")); cout << (*(q1)).c_str() << endl; Shared_ptr q2(new string("我是string2")); q2.swap(q1); cout << (*(q1)).c_str() << endl; return 0; }



