【C++】初识模板

目录

一、泛型编程

二、函数模板

1、什么是函数模板

2、函数模板格式

3、函数模板的原理

4、注意事项

三、类模板

1、类模板的定义格式

2、类模板的实例化

总结


一、泛型编程

泛型编程”这个概念最早就是来源于C++当初设计STL时所引入的模板(Template),而为什么要引入模板呢,因为STL要完成这样一个目标:设计一套通用的,不依赖类型的,高效的的算法(例如std::sort)和数据结构(例如std::list)。关于通用性,运行时多态(Polymorphism)可以做到(例如很多高级语言的继承(Inheritance)机制,接口(Interface)机制),但是C++作为一门相对底层的语言,对运行效率的要求是很严格的,而运行时多态会影响效率(例如成员函数只有在运行时才知道调用哪个),所以设计STL的人就创造了一种编译时多态技术,即模板。

我们在C语言阶段,要想写一个排序算法多半要使用Swap函数,但是我们如果要想排序不同的数据类型,就要写很多份Swap函数,虽然C++已经有了函数重载,但是还是十分的不方便

void Swap(int& left, int& right) 
{
     int temp = left;
     left = right;
     right = temp; 
}

void Swap(double& left, double& right) 
{
     double temp = left;
     left = right;
     right = temp; 
}

void Swap(char& left, char& right) 
{
     char temp = left;
     left = right;
     right = temp; 
}


......

我们就可以给编译器一个模子,告诉编译器如何生成。


由此引出了泛型编程

泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。

二、函数模板

1、什么是函数模板

模板(Template)在C++程序设计语言中,是指函数模板与模板[1],是一种参数化类型机制。JavaC#中的泛型与C++的模板大体对应,但也有一些功能上的显著差异(C++模板支持两者没有明确对应的模板模板参数和模板非类型参数,但不支持Java的通配符以及C#的泛型类型约束)。模板是C++的泛型编程中不可缺少的一部分。

模板是C++程序员绝佳的武器,特别是结合了多重继承运算符重载之后。C++的标准函数库提供的许多有用的函数大多结合了模板的概念,如STL以及iostream。——摘自维基百科

2、函数模板格式

    template<typename T1, typename T2,typename T3……>
    
    template<class T1, class T2, class T3……>

这是两种最常用的写法,typename和class在大多数情况下是没有区别的,可以通用,也可混用

其中里面的T1……Tn是可以任意命名的。一般是大写字母或者是单词首字母大写

3、函数模板的原理

函数模板主要经过下面几个过程

函数模板参数推演

推演参数实例化

那么我们上面的Swap函数传入的参数不同调用的是同一个函数吗?

答案是:不是的

 我们发现两次函数调用的地址是不同的。

函数模板的本质还是存在多个函数,不过我们只需要写一份就可以了,编译器会自动帮助我们写好其它类型的函数。

4、注意事项

  一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函
void Swap(int& left, int& right)
{
	int tmp = left;
	left = right;
	right = tmp;
}

template<class T>
void Swap(T& left, T& right)
{
	T tmp = left;
	left = right;
	right = tmp;
}

int main()
{

	int a = 3;
	int b = 4;
	double c = 3.3;
	double d = 4.4;
	Swap(a, b);
	Swap(c, d);

	return 0;
}

如果传入的是两个int它会优先调用,已经写好的函数,并不会再生成一份int类型的

函数模板不能进行隐式类型转换

template<typename T>
T Add(const T& x, const T& y)
{
	return x + y;
}



int main()
{

	Add(1, 2);
	Add(1.1, 2);

	return 0;
}

这里就会报错,并不是传参时报错,而是在推演实例化阶段报错

我们写好的函数是可以进行隐式类型提升的

我们可以将参数强转成同一类型

或者可以在函数名后面指定参数

Add<double>(1.1, 2);

模板不支持分离编译,声明放在.h中,定义放在.cpp中

模板在同一个文件中是可以声明和定义分离的。

三、类模板

1、类模板的定义格式

template<class T1, class T2, ..., class Tn>
class 类模板名
{
 
    // 类内成员定义

}; 


类模板与函数模板类似,不过是具体的类型用T来代表而已

2、类模板的实例化

类模板的实例化,我们以栈为例


template<typename T>
class Stack
{
public:
	//简单的写法
	/*Stack(int capacity = 4)
		:_a(nulptr)
		, _top(0)
		, _capacity(capacity)
	{}*/

	//延续C语言的写法,初始值为0
	Stack(int capacity)
		:_a(nullptr)
		, _top(0)
		, _capacity(0)
	{
		if (capacity > 0)
		{
			_a = new T[capacity];
			_capacity = capacity;
			_top = 0;
		}
	}

	~Stack()
	{
		delete[] _a;
		_top = _capacity = 0;
	}

	void Push(const T& x);

	bool Empty()const
	{
		return _top == 0;
	}

	void Pop()
	{
		assert(!Empty());

		_top--;
	}

	const T& Top()
	{
		assert(_top > 0);

		return _a[_top - 1];
	}

	size_t Size()
	{
		return _top;
	}

private:
	T* _a;
	size_t _top;
	size_t _capacity;
};


void test()
{
	Stack<int> st(0);
	st.Push(1);
	st.Push(2);
	st.Push(3);
	st.Push(4);
	st.Push(5);
	st.Push(6);
	st.Push(7);

	while (!st.Empty())
	{
		cout << "Size    " << st.Size() << endl;
		int top = st.Top();
		cout << "Top    " << top << endl;
		st.Pop();
	}

}

template<typename T>
void Stack<T>::Push(const T& x)
{
	if (_capacity == _top)//处理栈数据已经满了
	{
		size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;
		T* tmp = new T[newCapacity];
		if (_a)//_a不为0,代表原数组中有其它数据,需要拷贝
		{
			memcpy(tmp, _a, sizeof(T) * _capacity);
			delete[] _a;
		}
		_a = tmp;
		_capacity = newCapacity;
	}
	//插入数据
	_a[_top] = x;
	_top++;
}

int main()
{
    test();

    return 0;
}

模板类的实例化时必须要显示的指定类型,编译器无法推演出你的类的类型。

同时C++中,不到万不得已,千万不要使用malloc,realloc,calloc等

因为它们无法调用自定义类型的构造函数,如果使用了以上函数,我们也许可以使用定位new来解决构造函数的问题,但是无法解决调用析构函数的问题。

可是在我们的栈扩容时,需要使用类似于realloc函数的功能的操作符或者函数,但是C++中没有与realloc对标的操作符(new对标malloc  delete对标free)

因此:我们需要重新开辟一块空间,拷贝数据到新空间,然后释放新空间,让原来的指针指向新的空间,整个流程类似于realloc函数的过程。


总结


例如:以上就是今天要讲的内容,本文仅仅简单介绍了模板的初阶内容。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值