C++之泛型编程

模板:

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

首先来看C++的体系图

请添加图片描述

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

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

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

函数模板

基本范例

#include<iostream>
using namespace std;

namespace _nmspl{
    //template<typename T>  //T称之为类型模板
    template <class T> //语法规定,此处calss可以取代typename,这里的class没有类的含义
    T Sub(T tv1, T tv2){
        return tv1-tv2;
    }
}

int main(){
    cout<<_nmspl::Sub(3,5)<<endl;
    cout<<_nmspl::Sub(3.0,5.7)<<endl;
    return 0;
}

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

这里模板的几个规范如下

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

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

类模板必须显示指定类型

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

template<class T>
class Animal{
    
};  
template<class T>
class Cat : public Animal<T>{
    
};

//调用
Cat<int> cat;

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

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

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

    template<class T>
    Person<T>::Person(){
        
    }
    

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

解决办法:

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

  • 类模板中出现static关键字

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

应用:

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

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

template<class T>
class Myarray 

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

private:
	//一共可以容下多少元素
	int mCapacity; 
	//当前数组有多少元素
	int mSize;
	//保存数据首地址
	T* pAddr;

首先可以进行初始构造

//初始构造
	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];
		}

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

//重载等号运算符
	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;
      
	}

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

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

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

void push_back(T& data) {
	    //判断索引是否大于容器位置
		if (this->mSize >= this->mCapacity)
		{
			return;
		}
		this->pAddr[this->mSize] = data;
		this->mSize++;
	}

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++;
	}

最后再加上析构函数

~Myarray() {
		if (this->pAddr != NULL)
		{
			delete[] this->pAddr;
		}

整体实现代码如下

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;
		}
	}

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值