c++实验三


一、模板函数(compare)

所谓函数模板,实际上是建立一个通用函数,它所用到的数据的类型(包括返回值类型、形参类型、局部变量类型)可以不具体指定,而是用一个虚拟的类型来代替(实际上是用一个标识符来占位),等发生函数调用时再根据传入的实参来逆推出真正的类型。这个通用函数就称为函数模板(Function Template)。

在函数模板中,数据的值和类型都被参数化了,发生函数调用时编译器会根据传入的实参来推演形参的值和类型。换个角度说,函数模板除了支持值的参数化,还支持类型的参数化。

一但定义了函数模板,就可以将类型参数用于函数定义和函数声明了。说得直白一点,原来使用 int、float、char 等内置类型的地方,都可以用类型参数来代替。

1.一般模板函数

添加以下代码到queue.h

//函数模板
template<class Type>
int compare(const Type& v1, const Type& v2)
{
   if(v1<v2) return -1;
   if(v1>v2) return 1;
   return 0;
}

添加以下代码到queue.cpp

void testTemplate()
{
    double a = 1.2;
    double b = 1.5;
    cout<<"a=1.2,b=1.5,a<b:"<<compare(a,b)<<endl;       //-1

    double a1=3.0;
    double b1=2.0;
    cout<<"a1=3.0,b1=2.0,a1>b1:"<<compare(a1,b1)<<endl;         //1
}

int main(){
    testTemplate();
    return 0;
}

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

2.特化模板函数

有时通用的函数模板不能解决个别类型的问题,我们必须对此进行定制,这就是函数模板的特化。
添加以下代码到queue.h

//函数的特化
template<>
int compare<const char*>(const char * const &v1, const char * const &v2);

添加以下代码到queue.cpp

// 函数的特化
template<>
int compare<const char*>(const char * const &v1, const char * const &v2)
{
    return strcmp(v1,v2);
}
void testTemplate()
{
 //函数的特化
  	char str[10];
    char str1[10];
    strcpy(str,"abc");
    strcpy(str1,"def");
    cout<<"compare abc,def: "<<compare<const char*>(str,str1)<<endl;
    cout<<"compare def,abc "<<compare<const char*>(str1,str)<<endl;

}

int main(){
    testTemplate();
    return 0;
}

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

二、模板类Queue或Stack

C++ 中类模板的写法如下:

template <类型参数表>
class 类模板名{
成员函数和成员变量
};

类型参数表的写法如下:

class类塑参数1, class类型参数2, ...

1.模板类(Queue,Stack)

添加以下代码到queue.h

//类模板
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成全局函数
    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);
};

2.成员模板函数

类模板中的成员函数放到类模板定义外面写时的语法如下:

template <类型参数表>
返回值类型  类模板名<类型参数名列表>::成员函数名(参数表)
{
   ...
}

用类模板定义对象的写法如下:

类模板名<真实类型参数表> 对象名(构造函数实际参数表);

如果类模板有无参构造函数,那么也可以使用如下写法:

类模板名 <真实类型参数表> 对象名;

添加以下代码到queue.h

template<class Type>
void Queue<Type>::destroy()
{
    while (!empty()) {
        pop();
    }
}

template<class Type>
void Queue<Type>::pop(){
    if(head==0) return;
    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);
//    for(It it=beg;it!=end;it++)  //或者
//    {
//        push(*it);
//    }
}

template<class Type> template<class It> void Queue<Type>::copy_items(It beg,It end)     //beg输入的data的首地址data end是data的尾地址data+5
{
    while(beg!=end){
        push(*beg);
        ++beg;
    }
}

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

C++中的模板特化不同于模板的实例化,模板参数在某种特定类型下的具体实现称为模板的特化。模板特化有时也称之为模板的具体化,分别有函数模板特化和类模板特化。
模板成员函数特化:
添加以下代码到queue.h

template<class Type>
void Queue<Type>::pop(){
    if(head==0) return;
    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();

模板类特化:
添加以下代码到queue.cpp

//类部分模板特化
template<>
void Queue<const char*>::push(const char * const &val){
    char* new_item = new char[strlen(val)+1];           //加个结束符号 存得是new_item的地址
    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->item;  //删除的是new_item
    delete p;
}

实现
添加以下代码到queue.cpp

void testTemplate()
{
    Queue<int> qt;                  //class queue的使用
    double d = 3.3;
    qt.push(1);                     //1
    qt.push(d);                     //3  int定义为整形
    qt.push(10);                    //10
    cout<<qt<<endl;

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

    vector<int> vi(data,data+5);            //0,3,6,9,0
    qi.assign(vi.begin(),vi.end());
    cout<<endl;
    cout<<qi;

    Queue<const char*> qst;
    char str[10];
    strcpy(str,"I am");     //string copy的缩写
    qst.push(str);
    strcpy(str,"03");
    qst.push(str);
    strcpy(str,"huang");
    qst.push(str);
    cout<<endl<<qst<<endl;

}

int main(){
    testTemplate();
    return 0;
}

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

三、模板类AutoPtr

智能指针的一种通用实现技术是使用引用计数。智能指针类将一个计数器与类指向的对象相关联,引用计数跟踪该类有多少个对象的指针指向同一对象。智能指针主要用于管理在堆上分配的内存,它将普通的指针封装为一个栈对象。当栈对象的生存周期结束后,会在析构函数中释放掉申请的内存,从而防止内存泄漏。

1.构造函数

添加以下代码到autoptr.h

template<class T>
class AutoPtr
{
public:
    AutoPtr(T* pData);//构造函数
};

template<class T>
AutoPtr<T>::AutoPtr(T* pData)
{
    m_pData = pData;        //new一个存用户数的地方
    m_nUser = new int(1);       //new一个初始化的int
}

2.析构函数

添加以下代码到autoptr.h

template<class T>
class AutoPtr
{
public:
    AutoPtr(T* pData);//构造函数
    ~AutoPtr();//生命周期结束 析构函数
};

template<class T>
AutoPtr<T>::~AutoPtr(){
    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.h

template<class T>
class AutoPtr
{
public:
    AutoPtr(T* pData);//构造函数
    AutoPtr(const AutoPtr<T>& h);			//拷贝构造函数
    AutoPtr<T>& operator=(const AutoPtr<T>& h);//拷贝构造函数
    ~AutoPtr();//生命周期结束 析构函数
};

//拷贝构造函数
template<class T>
AutoPtr<T>::AutoPtr(const AutoPtr<T>& h)
{
    m_pData = h.m_pData;
    m_nUser = h.m_nUser;
    (*m_nUser)++;
}
//拷贝构造函数
template<class T>
AutoPtr<T>& AutoPtr<T>::operator=(const AutoPtr<T>& h)
{
    if(this==&h) return *this;
    decrUser();
    m_pData = h.m_pData;
    m_nUser = h.m_nUser;
    (*m_nUser)++;
    return *this;
}

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

添加以下代码到autoptr.h

template<class T>
class AutoPtr
{
public:
    AutoPtr(T* pData);//构造函数
    AutoPtr(const AutoPtr<T>& h);
    AutoPtr<T>& operator=(const AutoPtr<T>& h);
    ~AutoPtr();//生命周期结束 析构函数
    T* operator->(){        //指针运算符重载
        return m_pData;
    }
    T& operator*(){
        return *m_pData;
    }
    const T& operator *() const{
        return *m_pData;
    }
    const T* operator ->() const{       //返回的指针不能被改变
        return m_pData;
    }

private:
    void decrUser();
     T * m_pData;
    int * m_nUser;
};

5.主函数调用AutoPtr

添加以下代码到main.cpp。
CMatrix.h ,CMatrix.cpp自行添加到文件夹中

#include "autoptr.h"
#include "cmatrix.h"
void TestAutoPtr()
{
    AutoPtr<CMatrix>h1(new CMatrix);
    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);          //h2进行修改 0行第1列改成10
    cout<<*h1<<endl<<*h2<<endl;             //1 10 3 4 5 6  h2跟h1指向了同一个地方
}


int main(){
//    testTemplate();
    TestAutoPtr();
    return 0;
}

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1.声明一个动物基类Animal,私有整型成员变量年龄age,请定义一个派生类Dog,在其成员函数SetAge(int n)中直接给age赋值,测试下看是否会出问题?如何解决? 2.设计一个单基继承的类层次程序,用Person类派生出Student类,增加属性学号index和年级level。Person类中至少有姓名name、年龄age等数据成员,以及构造函数、输出函数等,其余成员函数根据需要添加。在主函数中进行测试。 3.定义一个学生类Student和教师类Teacher,学生类有姓名name、学号index等数据成员,教师类有姓名name、工作证号workID、职称title、课程course、周学时hoursPerWeek等数据成员。再定义一个助教类TeachingAssistant,多继承于学生类和教师类,该类可以使用学生类的全部数据成员,以及教师类的课程和周学时数据成员。要求:每个类提供自定义的构造函数和析构函数,并通过同名函数ShowInfo来显示全部数据成员的值。在主函数中进行测试。 4.声明一个Person,包含姓名name和年龄age等私有数据成员以及相关的成员函数;由它派生出领导类Leader,包含职务position和部门department私有数据成员以及相关的成员函数;再由Person派生出工程师类Engineer,包含职务position和专业speciality私有数据成员以及相关的成员函数;再由Leader和Engineer类派生出主任工程师类Chairman。在主函数中测试各类对象初始化和信息输出,查看是否会出问题?如何解决?

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值