模板简要介绍,C++读书笔记

2024年2月3日   内容整理自《程序设计教程:  用C++语言编程 第三版》 陈家骏  郑滔

24年4月13日复习

---------------------------------------------------------------------------------------------------------------------------------

(一)函数模板

1)类属函数和类属类

  • 类属函数是指一个函数能够对不同类型的数据(参数)完成相同的操作。
  • 类属类是指一个类的成员类型可变,从而可以用它描述不同种类的对象。

C++是一个静态类型语言,它提供了多种实现类属函数的途径,其中包括:

  • 宏定义
  • 指针类型
  • 函数模板

其中宏定义虽然能实现类属函数的效果,但它毕竟不是函数,而只是在编译之前的文字替换,因此下面介绍指针参数和函数模板实现的类属函数。

2)用指针参数实现类属函数

#include<iostream>
using namespace std;
//用指针参数实现类属的排序函数(框架)


void sort(
        
        //1.先定义一个void* 类型的形参base,使得它能够接受任意待排序数组的首地址
        void *base,
        //2.count参数用于获得数组元素的个数
        unsigned int count,
        //3.element_size用于每个数据元素的尺寸
        unsigned int element_size,
        //4.最后定义一个函数指针类型的形参cmp,它从调用者处获得一个函数,这个函数功能是比较两个数组元素的大小
        bool (*less_than)(const void*,const void*)
        
        ){
    
    //(1)取第i个元素,i从0到count-1,下面是第i个元素的首地址
    (char*)base+i*element_size;
    
    //(2)比较第i个和第j个元素的大小:首先分别计算i和J的首地址,然后调用cmp指向的函数来比较这两个地址上元素的大小
    (*less_than)(
            (char*)base+i*element_size,(char*)base+j*element_size
            )//传入i和j的地址
            
    
    //(3)交换i和j,逐个字节交换这两个地址上的元素
    char* p1=(char*)base+i*element_size;//p1是i的地址
    char* p2=(char*)base+j*element_size;//p2是j的地址

    for(int k=0;k<element_size;k++){
        char temp=p1[k];
        p1[k]=p2[k];
        p2[k]=temp;
    }//逐个字节交换地址上的元素
  
}


int main(){
   
   return 0;
}

-------------------------------------------------------------------------------------------------------------------------------

下面的程序片段是利用上面定义的排序函数分别对int、double、以及A类型的数组进行排序

//比较int类型的元素大小
bool int_less_than(const void* p1,const void* p2){
    if( *(int *)p1<*(int *)p2  )
        return true;
    else
        return false;
}


//比较double类型的元素大小
bool double_less_than(const void* p1,const void* p2){
    if( *(double *)p1<*(double *)p2  )
        return true;
    else
        return false;
}

//比较A类型的元素大小
bool A_less_than(const void* p1,const void* p2){
    if( *(A *)p1<*(A *)p2  )
        return true;
    else
        return false;
}

---------------------------------------------------------------------------------------------------------------------------------

用指针实现类属函数的不足之处在于:除了数组首地址和数组元素个数外,需要定义额外的参数(元素尺寸和比较函数),并且要进行大量的指针运算,这不仅使得实现比较麻烦,而且使得程序易读性差和容易出错。另外,用指针实现类属函数也不便于编译程序进行类型检查。。

3)用函数模板实现类属函数

C++中,实现类属函数的另外一种方法是用函数模板,function template是指带有类型参数的函数定义,格式如下:

template <class T1,class T2...,class Tn>
<返回值类型>  <函数名> (<形参列表>){
    ...
}

例子:

//用模板实现类属的排序函数框架
template <class T>//T是数组的元素类型,可以是int、double、A。。。
void sort(T elements[],unsigned int count){
    //取第i个元素
    elements[i];
    //比较第i个和第j个元素的大小
    elements[i]<elements[j]
    
    //交换第i和第j个元素
    T temp=elements[i];
    elements[i]=elements[j];
    elements[j]=temp;
}//sort
...
int a[100];
sort(a,100);//对int类型数组进行排序
double b[200];
sort(b,200);//对double类型数组进行排序
A c[300];
sort(c,300);//对A类型数组进行排序

4)函数模板的实例化

  • 函数模板可以隐式实例化,也可以显式实例化
  • 类模板只能显式实例化

显式实例化格式:

//模板函数max的声明
template <class T>
T max(T a,T b){
    
     return a>b?a:b;    
    
}
//模板函数max的调用(显式)
max<double>(1.4,3.5);

5)除了类型参数外,模板也可以带有非类型参数

#include<iostream>
using namespace std;

//第一个参数T是类型参数,取值为(int,double,float等),第二个参数是普通参数
//模板函数定义
template <class T,int size>
void function(T t){
    T temp[size];
}
//模板函数调用示例
int main(){
    function<float,10>(8.9);
    //T类型为float,size为10的,参数为(float t)且取t=8.9的函数
   return 0;
}

(二)类模板

如果一个类的成员类型可变,则该类成为类属类。在C++中,类属类一般用类模板实现。

类模板(class template)是指带有类型参数的类定义

1)格式

template <class T1,class T2,...,class Tn>
class <类名>{
    ...
};
//对于在类内声明,类外定义的成员函数,应采用如下格式
template <class T1,class T2,...,class Tn>
<返回值类型> <类名><T1,T2,...Tn>::<成员函数名>(<参数列表>){
    ...
}

2)类模板示例练习01(自己动手打一遍才能记住)

1.模板类的定义

//定义一个可以表示各种元素的Stack类
template <class T>
class Stack{
    //private数据成员
    T array[100];//定义一个元素类型为T的,长度为100的数组array
    int top;
    //public功能函数
public:
    Stack(){ top=-1;}//构造函数
    void push(const T &x);//在类内声明,等一会需要再类外定义的成员函数,传入一个T类型的数据压入栈中
    void pop(T &x);//类型为T的元素x退栈
};

2.类外函数的定义

template <class T>
void Stack<T>::push(const T &x) {
    ...
}

template <class T>
void Stack<T>::pop(const T &x){
    ...
}

3.在main中使用该模板类的格式示例

//模板类调用示例
int main(){
   Stack<int> S1;//T被实例化为int,创建了一个类的对象S1
   int x;
   S1.push(10);
   S1.pop(x);
    
   Stack<double> S2;
   double y;
   S2.push(4.4);
   S2.pop(y);
   
   Stack<A>  S3;
   A m,n;
   S3.push(m);
   S3.pop(n);
   
   return 0;
}

3)类模板示例练习02

1.模板类的定义

template <class T,int size>//同时带有类型参数和非类型参数的模板类
class Stack{
    T array[size];//每个元素为T类型,大小为size的数据成员
    int top;
public:
    Stack(){ top=-1;}
    void push(const T &x);
    void pop(T &x);
};

2.类外函数的定义

template <class T,int size>
void Stack<T,size>::push(const T &x){
    
}

template <class T,int size>
void Stack<T,size>::pop(T &x){
    
}

3.在main中使用该模板类的格式示例

//模板类调用示例
int main(){
    
   Stack<int,100> S1;//创建一个最多有100个int类型的元素所构成的Stack对象
   Stack<double,44> S2;//创建一个最多有44个double类型的元素所构成的Stack对象

   return 0;
}

(三)类模板分文件编写(.h和.cpp)

1)第一种方式:直接包含源文件.cpp

原因:在.h中只有声明,对于模板类来说是没有创建的,所以编译器会找不到实例;假如直接“#include xx.cpp”由于该.cpp文件中已经包含了.h,所以代码是完整的。

2)第二种方式:(更常用)将源文件和头文件的内容写到一起,将后缀名改为.hpp

-------------------------------------------------------

  写在最后,这几天零零散散看了C++里面的模板和容器,因为这章比较难,看The Cherno就看不太懂了,视频换成了b站的黑马程序员,加上读教材,磨了好几天终于有一点点明白了,OK。因为宅家太穷又去打了5天零工,每天11点睡5点半起床,洗把脸就得骑车去卡点上班。然后,如果说Cherno的C++系列是适合有编程经验的精华教程,精灵密道,那么黑马就是适合小白提升能力的高速大马路。黑马的那个老师用的软件是什么不知道,就是看起来非常舒服,小巧便捷,启动快,截图如下,感觉学编程这样搞,整洁、有秩序、有逻辑,会让自己的认知加速。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值