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