C++实验三:模板

一、模板函数(compare)

1. 一般模板函数

首先写一个判断是否相等的模板函数,注意声明和函数体都写在头文件中。

//模板函数需要将声明和函数体都放在头文件中
//一般模板函数
template<class T>
bool compare(const T& a, const T& b)
{
	return a == b;
}

2. 特化模板函数

使用模板时会遇到一些特殊的类型需要特殊处理,不能直接使用当前的模板函数,所以此时我们就需要对该类型特化出一个模板函数。
此处,该函数无法针对字符串类型使用,则需特化模板函数。

//上述模板函数对于字符串比较无法使用,所以需要特化
//格式:template<>不用写类型
//      函数名<特化类型>(特化类型 参数1, 特化类型 参数2,...)
//注意声明必须放在头文件中,实例化必须在cpp文件中
template<>
bool compare<const char*>(const char* const& a, const char* const& b);
template<>
bool compare<const char*>(const char* const& a, const char* const& b)
{
	return strcmp(a, b) == 0;
}

3. 测试

#include<iostream>
#include"queue.h"
using namespace std;
int main()
{
	const int a = 1;
	const int b = 1;
	const char* c = "qwe", * d = "asd";
	cout << compare(a, b) << endl;
	cout << compare(c, d);
}

运行结果:
在这里插入图片描述

4. 小结

一般特化函数格式

template< 模板参数表>
类型名 参数名(参数表)
{
	函数体的定义
}

特化模板函数注意点

  1. 使用模板特化时,必须要先有基础的模板函数(就是上面第一个模板函数)
  2. 使用特换模板函数时格式有要求:
    2.1. template 后直接跟<> 里面不用写类型
    2.2. 函数名<特化类型>(特化类型 参数1, 特化类型 参数2 , …) 在函数名后跟<>其中写要特化的类型
  3. 特化的函数的函数名,参数列表要和原基础的模板函数想相同,避免不必要的错误
  4. 声明必须放在头文件中,实例化必须在cpp文件中

函数模板与函数的区别

  1. 函数模板本身在编译时不会生成任何目标代码,只有由模板生成的实例会生成目标代码。
  2. 被多个源文件引用的函数模板,应当连同函数体一同放在头文件中,而不能像普通函数那样只将声明放在头文件中。
  3. 函数指针也只能指向模板的实例,而不能指向模板本身。

二、模板类Queue或Stack

1. 模板类(Queue,Stack)

类模板格式:

template<模板参数表>
class 类名
{
	类成员声明
};

如类模板QueueItem

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;
	}
};

模板参数表中参数可以声明为该模板类的友元类
如以上代码第6,7行的友元类
可通过typedef或者using对实例化的类模板定义别名

2. 成员模板函数

  • 在类模板中直接声明然后实例化
  • 如果需要在类模板以外定义其成员函数,则要采用以下的形式(现在类模板中声明成员函数):
template<模板参数表>
类型名 类名<模板参数标识符列表>::函数名(参数表){}
template<class Type> c1ass 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);
};
template<class Type> template<class It> 
void Queue<Type>::assign(It beg,It end)
{
	destroy();
	copy_items(beg,end);
}

3. 模板特化:模板函数特化、模板成员函数特化、模板类特化

3.1. 模板函数特化

模板函数特化见目录一、模板函数 2.特化模板函数

3.2. 模板成员函数特化

头文件中声明

template<>
void Queue<const char*>::push(const char * const &val) ;
template<>
void Queue<const char*>::pop();

cpp文件中实现

template<>
void Queue<const char*>::push(const char * const &val)
{
	char* new_item = new char[str1en(va1)+1];
	strncpy(new_item, val, strlen(va1)+1);
	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;
}

3.3. 模板类特化

//普通类模板
template<模板参数表>
class 类名
{
	类成员声明
};

//模板类特化
template<> 			//<>里为空
class 类名<特化类型> //<>里指明具体类型
{
	类成员声明
};

4. 代码:

queue.h

#ifndef QUEUE_H
#define QUEUE_H
#include <iostream>
using namespace std;
//模板函数需要将声明和函数体都放在头文件中
//一般模板函数
template<class T>
bool compare(const T& a, const T& b)
{
	return a == b;
}
//上述模板函数对于字符串比较无法使用,所以需要特化
//格式:template<>不用写类型
//      函数名<特化类型>(特化类型 参数1, 特化类型 参数2,...)
//注意声明必须放在头文件中,实例化必须在cpp文件中
template<>
bool compare<const char*>(const char* const& a, const char* const& b);

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;
	}
};

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>::destroy()
{
	while (!empty())
	{
		pop();
	}
}

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 = 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;
	}
}

#endif // QUEUE_H

queue.cpp

#include "queue.h"
#include <string.h>
template<>
bool compare<const char*>(const char * const &a, const char * const &b)
{
	return strcmp(a,b) == 0;
}

template<>
void Queue<const char*>::push(const char * const &val)
{
	char* new_item = new char[strlen(val)+1];
	strncpy(new_item, val, strlen(val)+1);
	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;
}

Test.cpp

#include<iostream>
#include"queue.h"
#include <vector>
#include "autoptr.h"
#include "CMatrix.h"
using namespace std;
int main()
{
	//const int a = 1;
	//const int b = 1;
	//const char* c = "qwe", * d = "asd";
	//cout << compare(a, b) << endl;
	//cout << compare(c, d);


	Queue<int> qt;
	double d = 3.3;
	qt.push(1);
	qt.push(d);
	qt.push(10); 
	cout << endl;
	cout << qt;

	short a[5] = { 0,3,6,9 };
	Queue<int> qi(a, a + 5);
	cout << endl;
	cout << qi;
	cout << endl;
	while (!qi.empty())
	{
		cout << qi.front() << " ";
		qi.pop();
	}
	vector<int> vi(a, a + 5);
	qi.assign(vi.begin(), vi.end());
	cout << endl;
	cout << qi;
	cout << endl;

	Queue<const char*> q1;
	q1.push("hi");
	q1.push("I'm");
	q1.push("Jerry");
	cout << q1;
	cout << endl;
	Queue<const char*> q2(q1);
	cout << q2;


    //AutoPtr<CMatrix> h1;
    //double data[6] = {1,2,3,4,5,6};
    //h1->Create(2,3,data);
    //AutoPtr<CMatrix> h2(h1);
    //(*h2).Set(0,1,10);
    //cout<<*h1<<*h2;
}

运行结果:
在这里插入图片描述

三、模板类AutoPtr

1. 构造函数

函数声明

AutoPtr(T* pData);

函数实例化

template<class T>
AutoPtr<T>::AutoPtr(T* pData)
{
	m_pData = pData;
	m_nUser = new int(1);
}

2. 析构函数

~AutoPtr()
	{
		decrUser();
	}
void decrUser();
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;
	}
}

3. 拷贝构造函数

函数声明

AutoPtr(const AutoPtr<T>& h);

函数实例化

template<class T>
AutoPtr<T>::AutoPtr(const AutoPtr<T>& h)
{
	m_pData = h.m_pData;
	m_nUser = h.m_nUser;
	(*m_nUser)++;
}

4. 等号、->、*等运算符重载

AutoPtr<T>& operator=(const AutoPtr<T>& h);
T* operator->()
{
	return m_pData;
}
T& operator*()
{
	return *m_pData;
}
const T& operator *()const
{
	return *m_pData;
}
const T* operator ->()const
{
	return m_pData;
}
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)++;
}

5. 主函数调用AutoPtr

#include<iostream>
#include"queue.h"
#include <vector>
#include "autoptr.h"
#include "CMatrix.h"
using namespace std;
int main()
{
	//const int a = 1;
	//const int b = 1;
	//const char* c = "qwe", * d = "asd";
	//cout << compare(a, b) << endl;
	//cout << compare(c, d);


	//Queue<int> qt;
	//double d = 3.3;
	//qt.push(1);
	//qt.push(d);
	//qt.push(10); 
	//cout << endl;
	//cout << qt;

	//short a[5] = { 0,3,6,9 };
	//Queue<int> qi(a, a + 5);
	//cout << endl;
	//cout << qi;
	//cout << endl;
	//while (!qi.empty())
	//{
	//	cout << qi.front() << " ";
	//	qi.pop();
	//}
	//vector<int> vi(a, a + 5);
	//qi.assign(vi.begin(), vi.end());
	//cout << endl;
	//cout << qi;
	//cout << endl;

	//Queue<const char*> q1;
	//q1.push("hi");
	//q1.push("I'm");
	//q1.push("Jerry");
	//cout << q1;
	//cout << endl;
	//Queue<const char*> q2(q1);
	//cout << q2;


    AutoPtr<CMatrix> h1;
    double data[6] = {1,2,3,4,5,6};
    h1->Create(2,3,data);
    cout << *h1 << endl;
    AutoPtr<CMatrix> h2(h1);
    (*h2).Set(0,1,10);
    cout << *h1 << endl << *h2;
}

运行结果如下:h2通过拷贝构造函数创建(拷贝h1),则h2调用Set方法后改变的是同一个地址的值,所以最后h1改变了,h1和h2输出结果相同。
在这里插入图片描述

6. 总结

在C++中,动态内存的管理是用一对运算符完成的:new和delete,new:在动态内存中为对象分配一块空间并返回一个指向该对象的指针,delete:指向一个动态独享的指针,销毁对象,并释放与之关联的内存。

动态内存管理经常会出现两种问题:一种是忘记释放内存,会造成内存泄漏;一种是尚有指针引用内存的情况下就释放了它,就会产生引用非法内存的指针。

为了更加容易(更加安全)的使用动态内存,引入了智能指针的概念。智能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值