C++ 函数模板与类模板

本文深入探讨了C++中的模板,包括函数模板的使用,模板实参推演,泛化与特化,以及类模板的声明和实例化。通过实例展示了模板如何简化代码并提供类型通用性,同时讲解了模板在栈操作中的应用,如SeqStack模板栈的实现。

函数模板

函数模板可以用来创建一个通用功能的函数,以支持多种不同形参,简化重载函数的设计

函数模板定义如下:
template<模板参数表>返回类型 函数名(形式参数表)
{……;}//函数体

<模板参数表>尖括号中不能为空,参数可以有多个,用逗号分开

模板参数主要是模板类型参数

模板类型参数代表一种类型,由关键字class 或 typename(建议用typename)后加一个标识符构成,这两个关键字的意义相同,他们表示后面的参数名代表一个潜在的内置或用户定义的类型

函数模板根据一组实际类型或值构造出独立的函数的过程通常是隐式发生的,称为模板实参推演

为了判断出模板实参的类型类型,编译器需检查函数调用中提供的函数实参的类型,ia的类型为Int类型,dx的类型为double数组,都被用来决定每个实例的模板参数,该过程称为模板实参推演

在编译main()函数中,由编译器函数模板而生成的函数,称为模板函数,这两个概念须分清楚

简单举例

template<class T>
void fun(T a)
{
	T x, y;
	cout << "T type :" << typeid(T).name() << endl;
	cout << "a type :" << typeid(a).name() << endl;
}
int main()
{
	int x = 10;
	fun(x);
	fun(&x);
}

在这里插入图片描述
下面,将x地址传入会推演为int const *类型(const在星号左边),为了防止能力拓展,限制其指向修改


template<class T>
void fun(T a)
{
	T x, y;
	cout << "T type :" << typeid(T).name() << endl;
	cout << "a type :" << typeid(a).name() << endl;
}
int main()
{
	const int x = 10;
	fun(x);
	fun(&x);
}

在这里插入图片描述
这里也是同理

void fun(T a)
{
	T x, y;
	cout << "T type :" << typeid(T).name() << endl;
	cout << "a type :" << typeid(a).name() << endl;
}
int main()
{
	int x = 10;
	const int y = 10;

	int* xp = &x;
	const int* yp = &y;

	fun(xp); //int *
	fun(yp); //const int *
}

在这里插入图片描述

泛化与特化

template<class T>
void fun(T a) //完全泛化
{
	T x, y;
	cout << "T type :" << typeid(T).name() << endl;
	cout << "a type :" << typeid(a).name() << endl;
}
template<class T>
void fun(T* a) //部分特化版本
{
	T x, y;
	cout << "T type :" << typeid(T).name() << endl;
	cout << "a type :" << typeid(a).name() << endl;
}
template<>
void fun<char*>(char* a) //完全特化版本
{
}

部分特化版本例子

template<class T>
void fun(T* a) //部分特化版本
{
	//T x, y;  当传入y T属于常变量(const int x,y) 需要进行初始化
	cout << "T type :" << typeid(T).name() << endl;
	cout << "a type :" << typeid(a).name() << endl;
}
 
 
int main()
{
	int x = 10;
	const int y = 10;//实际上推演会有const
	fun(&x);
	fun(&y);
}

在这里插入图片描述
在这里系统无法感知到,传入&y参数后,T属于const类型,但是编译器并没有丢失const类型

同样是部分特化版本

template<class T>
void fun(const T* a) //部分特化版本
{
	T x = 0, y = 0;
	cout << "T type :" << typeid(T).name() << endl;
	cout << "a type :" << typeid(a).name() << endl;
}


int main()
{
	int x = 10;
	const int y = 10;
	fun(&x);
	fun(&y);
}

在这里插入图片描述

模板推演

template<class T>
void fun(T a) 
{
	T x = 0, y = 0;
	cout << "T type :" << typeid(T).name() << endl;
	cout << "a type :" << typeid(a).name() << endl;
}

int main()
{
	int x = 10;
	const int y = 10;
	int* xp = &x;
	const int* yp = &y;

	fun(&xp);
	fun(&yp);
}

const 修饰指向y的能力
在这里插入图片描述

template<class T>
void fun(T& a)
{
	T x = 0, y = 0;
	cout << "T type :" << typeid(T).name() << endl;
	cout << "a type :" << typeid(a).name() << endl;
}


int main()
{
	int x = 10;
	const int y = 20;
	fun(x); //T int //a int& 
	fun(y); //T const int //a const int&
	return 0;
}

下面代码中,int *&是正确的,但是如果出现int&*则错误,因为引用本身属于一个指针,使用一个一级指针去指向引用是错误的;我们只能引用一个指针

template<class T>
void fun(T& a)
{
	T x = 0, y = 0;
	cout << "T type :" << typeid(T).name() << endl;
	cout << "a type :" << typeid(a).name() << endl;
}

int main()
{
	int x = 10;
	const int y = 20;
	int* xp = &x;
	const int* yp = &y;

	fun(xp); //T int* //a -> int* & 
	fun(yp); //T cosnt int* //a -> const int* &
	return 0;
}

这里是错误的,因为&a 属于常量,而我们推演得到int*无法通过非常量引用常量,所以是错误的,无法编译通过

template<class T>
void fun(T& a)
{
	T x = 0, y = 0;
	cout << "T type :" << typeid(T).name() << endl;
	cout << "a type :" << typeid(a).name() << endl;
}


int main()
{
	int x = 10;
	const int y = 20;
	
	fun(&x); 
	fun(&y); 
	return 0;
}

模板类

假如我们定义一个栈

template<class T>
class Stack
{
	T* data;
	size_t count;
public:
	void Push(const T& x);
};

int main()
{
	Stack<int> st;
}

类模板不存在推演过程,需要我们将类型给出

class Stack<int>
{
	typedef int T;
	//c11标准 与上面相同:
	using T = int; 

	T* data;
	size_t count;
public:
	void Push(const T& x);
}

class Stack<double>
{
	typedef double T;
	//c11:
	using T = double; 

	T* data;
	size_t count;
public:
	void Push(const T& x);
}

在编译过程中,在主程序中我们告知处理int类型,编译过程会生成上面的代码

template<class T,int N>
class Stack
{
	T data[N];
public:
	void Push(const T& x);
};

int main()
{
	Stack<int,100> st;
	Stack<int, 10> is;
}

对于创造的stis两个对象,类型是不一样的,这取决于生成过程的重命名规则

编译之后,两种类型完全不同,N终究会使用常量进行替换

class Stack<int,100>
{
	T data[100];
public:
	void Push(const T& x);
};
class Stack<int,10>
{
	T data[10];
public:
	void Push(const T& x);
};


int main()
{
	Stack<int,100> st;
	Stack<int, 10> is;
}

类模板中给出的函数,都是模板函数,如果我们没有调用该函数,则编译器不会对其进行实例化;继而会导致模板类编写过程编译可以通过,但是通过调用则会出现错误

模板栈练习

#define SEQ_INIT_SIZE 10 //默认大小
#define SEQ_INC_SIZE 2
template<class T>
class SeqStack
{
private:
	T* base;
	T* pos;
	size_t maxsize;
public:
	SeqStack(int sz = SEQ_INIT_SIZE) :maxsize(sz > SEQ_INIT_SIZE ? sz : SEQ_INIT_SIZE)
	{
		base = pos = (T*)malloc(sizeof(T) * maxsize);
		if (base == NULL) exit(0);
	}
	~SeqStack()
	{
		free(base);
		base = pos = NULL;
	}
	int Get_Size()
	{
		return (pos - base) / sizeof(pos);
	}
	int Get_capacity()
	{
		return maxsize;
	}
	bool Is_Empty()
	{
		return base == pos;
	}
	void push(T t)
	{
		*pos = t;
		pos++;
	}
	T Pop()
	{
		if (Is_Empty())
		{
			return -1;
		}
		T ret = *pos;
		pos--;
		return ret;
	}
	T Top()
	{
		if (Is_Empty())
		{
			return -1;
		}
		return *pos;
	}
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值