模板与自动指针
一、模板函数(compare)
引出问题
函数重载通常用于处理不同的数据类型完成类似的操作,但是有时一个操作可以处理不同的数据类型,这时重载就会出现,参数不同,方法相同的问题。而这时如果能够写一段通用代码处理多种不同数据,便会使代码可重用性大大提高。
使用函数模板就是为了这一目的。程序员只需对函数模板编写一次,然后基于调用函数时提供的参数类型,编译器会自动产生相应的函数来正确的处理该类型的数据,又称为函数模板的实例化。
注意:函数模板本身在调用前不会生成任何目标代码,只有在调用时才生成具体的目标代码。
参数化设计:
通用代码不受数据类型的影响,并且可以自动适应数据类型的变化。
模板支持参数化设计
参数化多态性:
就是将程序所处理的对象的类型参数化,使得一段程序可以用于处理不同类型的对象。
通过模板实现参数化多态性
1.一般模板函数
定义形式:
template<模板参数表>
类型名 函数名(参数表)
{
函数体的定义
}
所有函数模板的定义都是用关键字template开始。
模板参数表中的类型参数可以用来指定函数模板本身的形参类型、返回值类型,以及声明函数中的局部变量。
模板参数表由用逗号分割的模板参数构成,可以包括以下内容:
1) class(或template)标识符,指明可以接受一个类型参数,可以是预定义类型或自定义类型;
2)类型说明符 标识符,指明可以接受一个由 ”类型说明符“所规定类型的常量作为参数;
3)template<参数表> class 标识符,指明可以接受一个类模板名u走位参数。
举例,代码如下:
定义一个可以比较多种数据类型的模板函数
template<class T>,定义一个数据类型T,在函数中就可以像int、char一样使用这个数据类型
template<class T>
int compare(const T &v1, const T &v2){
if(v1<v2) return -1;
if(v1>v2) return 1;
return 0;
}
测试,将int型和double型输入函数测试。
void test1(){
int a1 = 2, a2 = 4;
cout<<compare(a1,a2);
double b1 = 2.0, b2 = 4.0;
cout<<compare(b1,b2);
}
2.特化模板函数模板
模板函数对某特定数据类型需要特殊处理,参数在某种数据特定类型下的具体实现称为模板的特化
对于模板函数compare(),在比较字符串类型时,如果实参为字符串的字符指针,方法显然不能使用数值型比较方法,否则比较的是指针的大小,所以在这种特殊情况下,要将模板特化。
注意:特化的模板函数声明可以写在头文件中,但是函数体实现只能写在cpp文件中。
举例代码如下:
模板特化函数实现时,参数表为空,函数名后标明特化处理的数据类型,本例<const char*>
void test1(){
int a1 = 2, a2 = 4;
cout<<compare(a1,a2);
double b1 = 2.0, b2 = 4.0;
cout<<compare(b1,b2);
const char *pa="sadf",*pb="wert";
cout<<compare(pa,pb);
}
template<>//写在cpp文件
int compare<const char*>(const char * const &v1, const char * const &v2){
return strcmp(v1,v2);
}
二、模板类(Queue)
模板类,使得类中的某些数据成员、某些成员函数的参数、返回值或局部变量能取不同类型。
声明语法形式:
template<模板参数表>
class 类名
{
类成员声明
}
注意:
1、类成员的声明方法与普通类几乎相同,只是还可以用到模板的类型参数
2、如果需要在模板类以外定义其成员函数,需要采用以下形式:
template<模板参数表>
类型名 类名<模板参数标识符列表>::函数名(参数名)
3、模板类声明自身不是一个类,只有被引用时,模板才根据引用的需要生成具体类。
4、用模板类建立对象时,语法如下:
模板名<模板参数表> 对象名;
1.成员模板函数
前面已经介绍了模板函数,那么定义在类内的模板函数即为成员模板函数,此时参数类型即包括类的参数类型,又包括模板自身的参数类型。
类模板内声明
声明与正常成员函数相比多了template<class It>
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 It> 又有模板函数的template<class It>
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;
}
}
2.模板特化
1.模板成员函数特化
前面已经提到,类模板的类型参数可以给它的成员使用,当成员函数使用了类型参数,可能会遇到前面说到的某种数据类型要特殊处理的问题,那么就会存在模板成员函数特化的情况
如下当向队列中添加或删除元素时,如果实参是字符串对应的指针,那么需要特殊处理
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 &);
template<>
void Queue<const char*>::pop();
2.模板类特化
如同模板函数特化一样,模板类对某种数据类型进行特殊处理时,将模板类进行特化。
将上面的模板类可以如下特化:
template<>
class Queue<const char*>{
public:
void Push(const char* str){real_queue.push(str);}
void Pop(){real_queue.pop();}
bool isEmpty() {return real_queue.empty();}
string front() const {return real_queue.front();}
friend ostream & operator<<(ostream& os, Queue<const char*> &que){
os<<que.real_queue;
}
// const string &front() const{return real_queue.front();}
private:
Queue<string> real_queue;
};
那么特化时就会面临,两种情况
1)将所有模板成员函数特化
2)将部分模板成员函数特化
三、类模板实现智能指针 AutoPtr
对内存的思考
C++ 程序中的内存分为两个部分:
1)栈:在函数内部声明的所有变量都将占用栈内存。
2)堆:这是程序中未使用的内存,在程序运行时可用于动态分配内存。
栈中的变量使用结束,系统会自行释放它们的内存,而堆中的变量使用完后,系统不会自动释放他们的内存,不用delete进行销毁,会一直占用堆的内存。
数据或指针不用new创建,会占用栈的内存,那么也不需要delete
而new是动态申请堆的内存,用new创建变量,就需要delete。
int *p= new int(10), p的内存在栈,int的内存在堆。
那么能否设计一个智能指针,解决在动态申请的内存用完后,能够自动释放内存呢。
指针的要求:
存放一个变量的地址,通过指针可以访问这个变量的值
又要求当使用结束后能够自动释放内存。
那么可以设计一个类,满足以下要求:
1)可以访问变量;
可以通过设计一个类成员,而类成员的数据类型又是不确定的,可以将类设计成类模板,类成员类型为参数类型。
2)能够自动释放内存;
需要设计一个标志,来判断是否存放的变量的值是否仍在使用,无则释放内存
3)同时还要满足指针的=, *及->运算。
template<class Type>
class AutoPtr
{
public:
构造函数
析构函数
运算符重载
private:
void declUser();
Type * m_nData;
int * m_nUser;
};
需要添加一个decrUser()函数来控制m_nUser,并当m_nUser为零时释放m_nData和m_nUser的内存
template<typename Type>
void autoPtr<Type>::declUser(){
--(*m_nUser);
if(*m_nUser == 0){
delete m_nData;
m_nData = 0;
delete m_nUser;
m_nUser = 0;
}
}
1.构造函数
template<typename Type>
autoPtr<Type>::autoPtr(Type* data){
m_nData = data;
m_nUser = new int(1); //int i =1;m_nUser = i;,函数结束 i释放内存,
}
template<typename Type>
autoPtr<Type>::autoPtr(const autoPtr<Type>& h){
m_nData = h.m_nData;
m_nUser = h.m_nUser;
(*m_nUser)++;
}
2.析构函数
template<typename Type>
autoPtr<Type>::~autoPtr(){
// if(*m_nUser == 0)
// cout<<"autoPtr destoried"<<endl;
}
v
3.运算符重载
Type* operator->(){
return m_nData;
}
Type& operator*(){
return *m_nData;
}
const Type* operator->() const{
return m_nData;
}
const Type& operator*() const{
return *m_nData;
}
template<typename Type>
autoPtr<Type>& autoPtr<Type>::operator=(const autoPtr<Type>& h){
declUser();
m_nData = h.m_nData;
m_nUser = h.m_nUser;
(*m_nUser)++;
}
void testautoptr(){
autoPtr<int> pt1(new int(10));
cout<<*pt1<<endl;
}