【C++ 3 】模板与自动指针

一、模板函数(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;

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值