C++模板

本文深入探讨C++中的模板,包括函数模板、类模板、非类型参数,以及模板的实例化、实参推演和特例化。通过示例解释模板在多文件工程中的应用,强调模板代码需放在头文件中。此外,讲解了简易C++STL向量容器的实现,以及空间配置器allocator的概念,展示了如何使用空间配置器管理内存和对象生命周期。
摘要由CSDN通过智能技术生成

理解函数模板

#include<iostream>
using namespace std;

/*
	C++函数模板(开发库用,类型参数化)
	优点:只用写一套代码实现逻辑即可,编译器没有减少工作,用户减少了工作

	模板的意义:对类型也可以进行参数化
	int sum(int a,int b){return a+b;} 仅仅对实参的值进行了参数化

	函数模板				出现关键字template模板参数列表,函数模板不进行编译,不知道类型
	模板的实例化			在调用点,根据用户指定的类型或者模板类型参数的推演推导出来需要	什么类型实例化,
	模板函数				实例化后出现的需要编译器编译的函数

	模板类型参数			typename/class
	模板非类型参数			

	模板的实参推演			可以根据用户传入的实参的类型,来推导出模板类型参数的具体类型
	模板的特例化(特化)	特殊的实例化,不是编译器提供的,而是用户提供
	函数模板、模板的特例化、非模板函数的重载关系 非模板函数 == 普通函数
*/

/*
	多文件工程
	连接错误,找不到模板,能找到普通函数和特例化函数
	模板的编译阶段实在调用时才会编译,*UND*
	模板代码必须放在头文件中,在源文件中直接进行include包含

	也可以使用template bool compare<int>(int,int)
	告诉编译器进行指定类型的模板实例化
	不用看到调用点,直接使用用户给定的类型实例化
*/

template<typename T>	//定义一个模板参数列表,定义了两个类型参数,分别用来接受一个类型
bool com(T a, T b)
{
	cout << "template compare" << endl;
	return a > b;
}
//针对com函数模板,提供const char*类型的特例化版本
template<>
bool com(const char* a, const char* b)
{
	cout << "compare <const char*> " << endl;
	return strcmp(a,b)>0;
}

template<typename T, typename E>	//定义一个模板参数列表,定义了两个类型参数,分别用来接受一个类型
bool compare(T a, T b)
{
	cout << "template compare" << endl;
	return a > b;
}
int main()
{
	//compare是一个函数模板的名称,不能直接调用compare(10, 20);
	
	//模板名加一个参数列表,才能变成一个函数名
	//函数调用点,编译器用用户指定类型,从原模版实例化一份函数代码出来
	/*
		模板函数
		从模板,用指定的类型,实例化出来的需要编译的函数
		bool compare<int,int>(int a, int b){return a > b; }
	*/
	compare<int,int>(10, 20);
	compare<double, int>(10, 20.0);

	//模板的实参推演,不会再生成一个int类型的函数,不然会导致重定义问题
	com(10, 20);

	//com(10,20.5)  无法推导出模板类型参数

	/*
		规定模板类型为int 将double等其他参数类型强制转化为int
	*/
	com<double>(10, 23.5);

	/*
		bool compare<const char*>(const char* a, const char* b)
		{
			return a > b; //仅仅是在比较地址,如何比较ASCII码顺序
						  // strcmp(a,b)>0
		}
	*/

	//对于某些类型来说,依赖编译器默认实例化的模板代码,代码处理逻辑是有错误的(字符串类型)
	//优先按照非模板函数处理
	com("aaa", "bbb");
	//发现尖括号处理成模板,先准备实例化模板,发现特例化模板,使用特例化模板
	com<const char*>("aaa", "bbb");
	return 0;
}

非类型参数

#include<iostream>
#include <algorithm>
using namespace std;

/*
	函数模板

	模板的非类型参数,必须是整数类型,整数和地址都可以
	冒泡排序的第二个SIZE参数,都是常量,只能使用不能修改



	类模板

*/
template<typename T,int SIZE>	//SIZE非类型参数
void sort(T* arr)
{
	for (int i = 0; i < SIZE - 1; ++i)
	{
		for (int j = 0; j < SIZE - 1 - i; ++j)
		{
			if (arr[j] > arr[j + 1])
			{
				int temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
		}
	}
}

int main()
{
	int arr[] = { 12,23,14,25,34,67,10,2 };
	//const 编译的时候就确定了  使用常变量就不符合规则
	const int size = sizeof(arr) / sizeof(arr[0]);
	sort<int,size>(arr);
	for (int val : arr)
	{
		cout << val << " "<< endl;
	}
}

类模板

#include<iostream>
using namespace std;

/*
	类模板
	类模板=> 实例化 => 模板类

	类模板的选择性实例化,不是所有都实例化,
	运行的时候,调用一个成员函数实例化一个函数
*/

template<typename T = int>//类型参数默认值
class SeqStack	//模板名称
{
public:
	//构造和析构函数名不用加<T>,其他出现模板的地方都加上类型参数列表
	SeqStack<T>(int size = 10)
		:_pstack(new T[size])
		,_top(-1)
		,_size(size)
	{

	}
	~SeqStack<T>()
	{
		delete[] _pstack;
		_pstack = nullptr;
	}
	SeqStack<T>(const SeqStack<T>& stack)
		:_top(stack._top)
		,_size(stack._size)
	{
		_pstack = new T[_size];
		for (int i = 0; i < top; ++i)
		{
			_pstack[i] = stack._pstack[i];
		}
	}
	SeqStack<T>& operator=(const SeqStack<T>& stack)
	{
		//防止自赋值
		if (this == &stack)
		{
			return *this;
		}

		delete[] _pstack;

		_top = stack._top;
		_size = stack._size;
		_pstack = new T[_size];

		for (int i = 0; i < top; ++i)
		{
			_pstack[i] = stack._pstack[i];
		}
	}
	void push(const T& val)
	{
		if (full())
		{
			expand();
		}
		_pstack[top++] = val;
	}
	void pop()
	{
		if (empty())
		{
			return;
		}
		--top;
	}
	T top()const
	{
		if (empty())
		{
			//抛出异常也代表函数逻辑结束返回,不需要保持类型一致
			throw "stack is empty!";
		}
		return _pstack[_top - 1];
	}
	bool full()const
	{
		return _top == _size;
	}
	bool empty()const
	{
		return _top == 0;
	}
private:
	T* _pstack;
	int _top;
	int _size;

	//顺序栈底层数组按2倍的方式扩容
	void expand()
	{
		//new 可以调用malloc构造函数 但是malloc只能开辟内存
		T* ptmp = new T[2 * _size];
		for (int i = 0; i < top; ++i)
		{
			_pstack[i] = stack._pstack[i];
		}
		delete[] _pstack;
		_pstack = ptmp;
		_size = _size * 2;
	}
};

//在模板类外定义模板函数
template<typename T>
void SeqStack<T>::push(const T& val)
{
	if (full())
	{
		expand();
	}
	_pstack[_top++] = val;
}
int main()
{



	return 0;
}

实现简易C++ STL向量容器Vector

#include<iostream>

using namespace std;

/*
	实现C++ STL里面的一个顺序容器vector 向量容器

	容器:
*/

template<typename T>
class vector
{
public:
	vector(int size = 10)
	{
		_first = new T[size];
		_last = _first;
		_end = _first + size;
	}

	~vector()
	{
		cout <<" 析构对象是" <<this->name << endl;
		delete[] _first;
		//_first = _last = _end = nullptr;
	}

	vector(const vector<T>& src)
	{
		int len = src._last - src._first;
		_first = new T[src._end - src._first];
		for (int i = 0; i < len; ++i)
		{
			_first[i] = src._first[i];
		}
		_last = _first + len;
		_end = _first + size;
	}

	vector<T>& operator= (const vector<T>& src)
	{
		if (this == &src)
		{
			return *this;
		}
		delete[] _first;
		int len = src._last - src._first;
		_first = new T[src._end - src._first];
		for (int i = 0; i < len; ++i)
		{
			_first[i] = src._first[i];
		}
		_last = _first + len;
		_end = _first + size;
		return *this;
	}

	//向容器末尾 添加元素
	void push_back(const T &val)
	{
		if (full())
			expand();
		*_last++ = val;
	}
	//从容器末尾删除元素
	void pop_back()
	{
		if (empty())
		{
			return;
		}
		//last本质是指向最后元素的后继位置,first和last之间存在的就是所有的元素
		_last--;
	}
	//返回容器末尾的函数值
	T back() const
	{
		//last存放的是最后一个元素的后继位置,-1之后再会变成最后一个元素
		//不能给last直接解引用,因为last所在的位置并不是有效的元素位置
		return *(_last - 1);
	}
	void show() { cout << "This is" <<name<< endl; }
	bool full() const { return _last == _end; }
	bool empty() const { return _last == _first; }
	int size() const{ return _last - _first; }
	int name;
private:
	T* _first;	//指向数组起始位置
	T* _last;	//指向数组中有效元素的后继位置
	T* _end;	//指向数组空间的后继位置


	//容器的二倍扩容
	void expand()
	{
		int size = _end - _first;
		T* ptmp = new T[2 * size];
		for (int i = 0; i < size; ++i)
		{
			ptmp[i] = _first[i];
		}
		_first = ptmp;
		_last = _first + size;
		_end = _first + 2 * size;
	}
};
int main()
{
	vector<int>vec;
	vec.name = 0;
	for (int i = 0; i < 20; ++i)
	{
		vec.push_back(rand() % 100);
	}

	while (!vec.empty())
	{
		cout << vec.back() << endl;
		vec.pop_back();
	}
	
	return 0;

}

空间配置器

#include<iostream>
using namespace std;

/*
	空间配置器 allocator
	template<class _Ty,class _Alloc=allocator<_Ty>>
	1.内存开辟
	2.内存释放
	3.对象构造
	4.对象析构
*/

//定义容器的空间配置器,和C++标准库的allocator实现一样
template <typename T>
class Allocator
{
public:
	Allocator()
	{

	}
	~Allocator()
	{

	}
	//负责内存开辟
	T* allocate(size_t size)
	{
		return (T*)malloc(sizeof(T) * size);
	}
	//负责内存释放
	void deallocate(void* p)
	{
		free(p);
	}
	//负责对象构造,再开辟好的内存上构造一个值为val的对象
	void construct(T* p, const T& val)
	{
		//定位new	new(buffer)typename[size]
		//指定的内存上,构建一个值为val的对象,会调用T类型的一个拷贝构造
		new(p)T[val];
	}
	//负责对象析构
	void destroy(T* p)
	{
		//调用了p指向的对象的析构函数
		p->~T();
	}

};


template<typename T,typename Alloc = Allocator<T>>
class vector
{
public:
	vector(int size = 10,const Alloc &alloc = Allocator<T>())
		:_allocator(alloc)
	{
		//只开辟有效元素,但是new会全部开辟
		//需要把内存开辟对象构造分开处理

		//不需要new构建无效的对象,所以放弃使用new,使用空间配置器来管理内存
		//_first = new T[size];

		_first = _allocator.allocate(size);
		_last = _first;
		_end = _first + size;
	}

	~vector()
	{
		//只析构有效元素,然后释放_first指针指向的堆内存
		//delete[] _first;
		
		//析构
		for (T* p = _first; p != _last; ++p)
		{
			//把_first指针指向的数组的有效元素进行析构操作
			_allocator.destroy(p);
		}
		//释放堆上的数组内存
		_allocator.deallocate(_first);
		_first = _last = _end = nullptr;
	}

	vector(const vector<T>& src)
	{
		int len = src._last - src._first;
		int size = src._end - src._first;
		//首先开辟空间,然后构造对象,都使用空间配置器完成
		//不能使用new来开辟空间,因为new会自动生成新对象
		//first = new T[src._end - src._first];
		
		//1.开辟空间
		_first = _allocator.allocate(size);
		for (int i = 0; i < size; ++i)
		{
			//_first[i] = src._first[i];
			//在已有的内存上构造对象
			_allocator.construct(_first + i, src._first[i]);
		}
		_last = _first + len;
		_end = _first + size;
	}

	vector<T>& operator= (const vector<T>& src)
	{
		if (this == &src)
		{
			return *this;
		}
		
		//delete[] _first;
		int len = src._last - src._first;
		//_first = new T[src._end - src._first];
		for (T* p = _first; p != _last; ++p)
		{
			//把_first指针指向的数组的有效元素进行析构操作
			_allocator.destory(p);
		}
		_allocator.deallocate(_first);

		int len = src._last - src._first;
		int size = src._end - src._first;
		//首先开辟空间,然后构造对象,都使用空间配置器完成
		//不能使用new来开辟空间,因为new会自动生成新对象
		//first = new T[src._end - src._first];

		_first = _allocator.allocate(size);
		for (int i = 0; i < size; ++i)
		{
			_allocator.construct(_first + i, src._first[i]);
		}
		_last = _first + len;
		_end = _first + size;
		return *this;
	}

	//向容器末尾 添加元素
	void push_back(const T& val)
	{
		if (full())
			expand();
		_allocator.construct(_last, val);
		_last++;
	}
	//从容器末尾删除元素
	void pop_back()
	{
		if (empty())
		{
			return;
		}
		//last本质是指向最后元素的后继位置,first和last之间存在的就是所有的元素
		_last--;
		_allocator.destroy(_last);
	}
	//返回容器末尾的函数值
	T back() const
	{
		//last存放的是最后一个元素的后继位置,-1之后再会变成最后一个元素
		//不能给last直接解引用,因为last所在的位置并不是有效的元素位置
		return *(_last - 1);
	}
	void show() { cout << "This is" << name << endl; }
	bool full() const { return _last == _end; }
	bool empty() const { return _last == _first; }
	int size() const { return _last - _first; }
	int name;
private:
	T* _first;			//指向数组起始位置
	T* _last;			//指向数组中有效元素的后继位置
	T* _end;			//指向数组空间的后继位置
	Alloc _allocator;	//容器的空间配置器对象

	//容器的二倍扩容
	void expand()
	{
		int size = _end - _first;
		//T* ptmp = new T[2 * size];
		
		T* ptmp = _allocator.allocate(2 * size);
		for (int i = 0; i < size; ++i)
		{
			//ptmp[i] = _first[i];
			_allocator.construct(ptmp + i, _first[i]);
		}
		//delete[] _first 会调用所有成员的析构,不管是否有效
		for (T* p = _first; p != _last; ++p)
		{
			_allocator.destroy(p);
		}
		_allocator.deallocate(_first);
		_first = ptmp;
		_last = _first + size;
		_end = _first + 2 * size;
	}
};

class Test
{
public:
	Test() { cout << "Test()" << endl; }
	~Test() { cout << "~Test()" << endl; }
	Test(const Test&) { cout << "Test(const Test&)" << endl; }
private:
};
int main()
{
	//new 不仅仅开辟空间,还会构造对象。
	vector<Test>vec;
	Test T1, T2, T3;

	cout << "_________________________" << endl;
	vec.push_back(T1);
	vec.push_back(T2);
	vec.push_back(T3);
	cout << "_________________________" << endl;
	//如果Test指向了外部资源,pop_back没有释放外部资源,只是使得指针回退,再次添加元素覆盖,无法释放开辟堆内存找不到指针,内存碎片
	//只需要析构对象,然后释放_first指针指向的堆内存。
	//将对象的析构和内存释放分离开
	vec.pop_back();
	cout << "_________________________" << endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值