• C++之泛型编程


    模板:

    建立通用的与数据类型无关的算法模板。可复用性增加,冗余减少。C++泛型编程的知识其实没有那么多,但是能够玩出的花式确实非常多,最好的就是 参考STL标准库的学习,这是泛型编程的巅峰之作!!

    首先来看C++的体系图

    请添加图片描述

    设计模式是通过增加代码复杂性,来换取代码的灵活性,而学习设计模式最好从项目中汲取经验,不然就等着面试的时候背一下八股文,应付一下也是无所谓的。

    泛型编程,比如STL,Boost库代表了C++顶尖开发者的水平。

    主要分为两个部分,函数模板和类模板

    函数模板

    基本范例

    #include
    using namespace std;
    
    namespace _nmspl{
        //template  //T称之为类型模板
        template  //语法规定,此处calss可以取代typename,这里的class没有类的含义
        T Sub(T tv1, T tv2){
            return tv1-tv2;
        }
    }
    
    int main(){
        cout<<_nmspl::Sub(3,5)<
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    模板的使用可以增强代码的可复用性,减少冗余,如上范例所示实现了一个简单的支持各种数据类型(如float,double,int,long等)的减法运算。

    这里模板的几个规范如下

    • 模板的定义是以template关键字开头
    • 模板参数T前面用typename修饰,此处typename可以用class修饰,因为此处class没有类的含义。
    • 模板参数T以及前面的修饰词都用<>括起来
    • 模板参数T可以换成任意其他表示符
    类模板

    函数模板在调用的时候,可以自动类型推导

    类模板必须显示指定类型

    类模板在继承的时候也需要指定具体的类型,因为类的对象需要分配内存,所以你需要具体的指定,或者再一次使用模板

    template
    class Animal{
        
    };  
    template
    class Cat : public Animal{
        
    };
    
    //调用
    Cat cat;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    所以我们所知道的vector,map等容器都是模板的实现。

    注意:
    • 在类模板的情况下,不要滥用友元函数,这样在Linux和windows下的编译情况是不一样的。

    • 类模板分离解决方案,在cpp文件中需要在类名前面加上

      template<class T>
      Person<T>::Person(){
          
      }
      
      • 1
      • 2
      • 3
      • 4

    但是编译过程会出现错误。因为你的模板cpp文件只进行了一次编译,还没有生成具体的函数,而你的主函数编译过后,调用连接器寻找的时候,找不到对应的执行函数。

    解决办法:

    就是include的时候,包含cpp文件而不是.h文件,这样模板cpp就可以进行二次编译,但是这样显得很蠢,那还要.h文件干什么呢
    所以就出现了一种新的方法,那就是hpp,将h文件和cpp文件的内容写到一起。

    • 类模板中出现static关键字

      • static关键字不归类模板所有,而是具体的类所有。所以你每生产一个具体的类之后,也会获得一个独有的关键字

    应用:

    • 重载是参数的重载,模板是数据类型的泛化

    这里我们尝试实现一个vector的泛型数组,首先是定义类

    template<class T>
    class Myarray 
    
    • 1
    • 2

    然后是成员,比如容量,当前元素个数,以及数组

    private:
    	//一共可以容下多少元素
    	int mCapacity; 
    	//当前数组有多少元素
    	int mSize;
    	//保存数据首地址
    	T* pAddr;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    首先可以进行初始构造

    //初始构造
    	Myarray(int capacity) {
    		this->mCapacity = capacity;
    		this->mSize = 0;
    		this->pAddr = new T(this->mCapacity);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    然后拷贝构造方法也可以实现一下

    //拷贝构造
    	Myarray(const Myarray& arr) {
    		//容量、大小赋值
    		this->mCapacity = arr.mCapacity;
    		this->mSize = arr.mSize;
    		//申请空间
    		this->pAddr = new T(this->mCapacity);
    		//数据拷贝
    		for (int i = 0; i < this->mSize; i++)
    		{
    			this->pAddr[i] = arr.pAddr[i];
    		}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    等号赋值构造也比较常用。所以把等号也重载一下

    //重载等号运算符
    	Myarray<T> operator=(const Myarray<T>& arr) {
    		
    		if (this->pAddr != NULL)
    		{
    			delete[] this->pAddr;
    		}
    		//容量、大小赋值
    		this->mCapacity = arr.mCapacity;
    		this->mSize = arr.mSize;
    		//申请空间
    		this->pAddr = new T(this->mCapacity);
    		//数据拷贝
    		for (int i = 0; i < this->mSize; i++)
    		{
    			this->pAddr[i] = arr.pAddr[i];
    		}
    		
    		return *this;
          
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    容器查找元素也可以通过myarry[i],所以把中括号重载一下

    T& operator[](int index) {
    		return this->pAddr[index];
    	}
    
    • 1
    • 2
    • 3

    容器一般有push_back函数,把push_back函数实现一下

    void push_back(T& data) {
    	    //判断索引是否大于容器位置
    		if (this->mSize >= this->mCapacity)
    		{
    			return;
    		}
    		this->pAddr[this->mSize] = data;
    		this->mSize++;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    push_back如果直接传值,类似于push_back(100),因为常量是右值,不能直接引用,需要重载一个push_back函数

    void push_back(T&& data) {
    	    //判断索引是否大于容器位置
    		if (this->mSize >= this->mCapacity)
    		{
    			return;
    		}
    		this->pAddr[this->mSize] = data;
    		this->mSize++;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    最后再加上析构函数

    ~Myarray() {
    		if (this->pAddr != NULL)
    		{
    			delete[] this->pAddr;
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    整体实现代码如下

    template<class T>
    class Myarray {
    public:
    	//初始构造
    	Myarray(int capacity) {
    		this->mCapacity = capacity;
    		this->mSize = 0;
    		this->pAddr = new T(this->mCapacity);
    	}
    	//拷贝构造
    	Myarray(const Myarray& arr) {
    		//容量、大小赋值
    		this->mCapacity = arr.mCapacity;
    		this->mSize = arr.mSize;
    		//申请空间
    		this->pAddr = new T(this->mCapacity);
    		//数据拷贝
    		for (int i = 0; i < this->mSize; i++)
    		{
    			this->pAddr[i] = arr.pAddr[i];
    		}
    	}
    	//运算符重载
    	T& operator[](int index) {
    		return this->pAddr[index];
    	}
    	//重载等号运算符
    	Myarray<T> operator=(const Myarray<T>& arr) {
    		
    		if (this->pAddr != NULL)
    		{
    			delete[] this->pAddr;
    		}
    		//容量、大小赋值
    		this->mCapacity = arr.mCapacity;
    		this->mSize = arr.mSize;
    		//申请空间
    		this->pAddr = new T(this->mCapacity);
    		//数据拷贝
    		for (int i = 0; i < this->mSize; i++)
    		{
    			this->pAddr[i] = arr.pAddr[i];
    		}
    		
    		return *this;
    	}
    	void push_back(T& data) {
    	    //判断索引是否大于容器位置
    		if (this->mSize >= this->mCapacity)
    		{
    			return;
    		}
    		this->pAddr[this->mSize] = data;
    		this->mSize++;
    	}
    
    
    	void push_back(T&& data) {
    		//判断索引是否大于容器位置
    		if (this->mSize >= this->mCapacity)
    		{
    			return;
    		}
    		this->pAddr[this->mSize] = data;
    		this->mSize++;
    	}
    	~Myarray() {
    		if (this->pAddr != NULL)
    		{
    			delete[] this->pAddr;
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    学会基础知识的你,如果想要深入了解,下一步的计划应该是STL库的学习。

  • 相关阅读:
    基于HTML+CSS制作一个简单的家乡网页制作作业,广州介绍旅游网页设计代码 学生个人html静态网页制作成品代码
    解决Jackson解析JSON时出现的Illegal Character错误
    轻取软考45分之软考信息系统项目管理师项目成本管理​章节学习笔记
    代码随想录算法训练营29期|day55 任务以及具体安排
    k8s网络插件之Calico
    lightdb22.3-oracle 内置包兼容增强
    4、【WebGIS实战】软件操作篇——数据导入及处理
    运筹学-单纯形法-代码实现(包含做题的每一步骤)
    零时科技 || EGD被黑客攻击损失超3.6万 BUSD,事件分析
    Kafka从入门到精通02
  • 原文地址:https://blog.csdn.net/suren_jun/article/details/127537318