模板

2023-05-30 11:41:23 浏览数 (1)

泛型

c 中引进了泛型的概念,而引用泛型也是为了解决了C语言中对不同类型的参数需要实现多个不同参数的麻烦。 而泛型只是提供一个模板而已,对于不同的函数或者类会自动实例化相应的函数或者类。

模板

模板分为函数模板类模板

函数模板

基本语法: template<typename T,......>typename也可以换成class,而T可以随便改变,不叫T也是可以的。

  • 对于c语言的函数,我们需要写不同类型的参数。即使c 中引入了函数重载,也是需要写多个重载函数。而模板的出现解决了这个问题。 下面就演示一下:
代码语言:javascript复制
cpptemplate<typename T>
T Add(const T& a, const T& b)
{
	return a   b;
}

当传的是int参数的时候,会自动推演出T为int,并且实例化出该函数,而这些工作都是编译器为我们做的。

但是

有时候需要我们显示的显示出类型。比如传这样的参数Add(1.1,2),那么编译器就无法推演出来。此时就需要我们显示的传入类型。Add<double>(1.1,2),就是在函数名字的后面加上一个<>,里面是显示传的类型。

当我们有显示类型的函数,还有一个模板参数的时候,那么会调用哪一个呢?结论是,当传入的参数是那个显示写的函数的时候,会调用显示写的,没有再去调用模板。

代码语言:javascript复制
cpptemplate<typename T>
T Add(const T& a, const T& b)
{
	cout << "模板" << endl;
	return a   b;
}
int Add(int& a, int& b)
{
	cout << "非模板" << endl;
	return a   b;
}

测试代码: int x = 1, y = 2; Add(x, y);

注意: 有时候会失败,可能的原因是权限问题,比如权限的放大会报错。欢迎评论区讨论。这里就不放相应的代码了。虽然我已经遇到过了。

类模板

为什么引用模板上文已经说了,这里就不再叙述。 本博主觉得和函数模板差别不是很大,只不过要显示的写出类型。类型随意,可以是自定义类型也可以为内置类型。类模板的定义和声明是不可以分在两个文件中的。 这里演示一个栈的类吧!

代码语言:javascript复制
cpptemplate<class T>
class Stack
{
public:
	Stack(size_t capacity=4)//构造函数
		:_a(nullptr)
		, _top(0)
		, _capacity(0)
	{
		if (capacity > 0)
		{
			_a = new T[capacity];
			_capacity = capacity;
		}
	}
	void Push(T x)//插入
	{
		if (_top == _capacity)
		{
			int NewCapacity = _capacity == 0 ? 4 : _capacity * 4;
			T* temp = new T[NewCapacity];
			if (_capacity == 0)
			{
				_a = temp;
				_capacity = NewCapacity;
			}
			else
			{
				memcpy(temp, _a, sizeof(T) * _capacity);
				_a = temp;
				_capacity = NewCapacity;
			}
		}
		_a[_top] = x;
		  _top;
	}
	void Pop()//删除
	{
		assert(_top > 0);
		--_top;
	}
	~Stack()//析构
	{
		delete[] _a;
		_a = nullptr;
		_capacity = _top = 0;
	}
private:
	T* _a;
	size_t _top;
	size_t _capacity;
};
int main()
{
	Stack<int> p1;//显示传入类型
	Stack<int> p2(100);
	return 0;
}

非类型模板参数

非类型的模板参数就是:这个模板的形参是一个常量。

代码语言:javascript复制
cpptemplate<class T,int N=20>//这里的int N=20就是一个非类型的模板参数
class A
{
	T name[N];
};

int main()
{
	A<int,30> a;
    return 0;
}

对于这个常量我们也是有要求,要求不能是浮点数,类对象,字符串

模板特化

什么是模板特化?为什么要引入模板特化? 先回答第一个问题,什么是模板特化——在原先已有模板的情况下,将参数T写成具体的类型。 特化的形式:

代码语言:javascript复制
cpptemplate<class T>//已有的模板
class A
{
	.........
}
//模板特化
template<>
class A<int*>
{
    ......
}
代码语言:javascript复制
cpptemplate<class T>
bool comp(T a,T b)
{
	return a>b;
}
template<>
bool comp<int>(int a,int b)
{
	return a>b;
}

对于类的模板的特化,还有一些其他的情况: 部分特化:

代码语言:javascript复制
cpptemplate<class T1,class T2>
class A
{
	T1 a;
	T2 n;
};
//只把第二个参数类型进行了特化处理
template<class T>
class A<T,int>
{
	T a;
	int b;
};

为什么要模板特化呢? 因为一些特殊的场景,比如比较大小的时候,我们要比较两个数的大小,但是如果此时传递的是指针那么就不能比较了,或者说要重新写一个,那么此时就可以特化一下。

代码语言:javascript复制
cppbool comp(T a, T b)
{
	return a > b;
}
int main()
{
	int x = 1, y = 2;
	cout << comp(x, y) << endl;
	cout << comp(&x, &y) << endl;
	return 0;
}

结果是0,1。我们想的是即使传的是地址也能进行比较,而不是对地址进行比较。 下面这样就可以了。

代码语言:javascript复制
cpptemplate<>
bool comp<int*>(int* a, int* b)
{
	return *a > *b;
}

模板的分离编译

模板是不支持分离编译的,即在模板的声明和定义不在同一个文件里面。 为什么它不支持分离编译呢? 通常情况下,当分离编译的时候,声明是放在.cpp里面的,定义是放在.h。 下面是代码变成可执行文件的过程:(简化) 预编译:.h文件的内容在.cpp中展开 编译:.cpp文件变成汇编代码,此时的模板.cpp里面参数T的类型不确定,所以不会有相应的指令代码。 汇编:二进制文件 链接:把所有的.cpp文件链接在一起生成可执行文件。 那么在链接的时候就会出现问题,我们只能找到声明,不能找到定义。有人会说,不是有.cpp里面的定义吗?——模板参数在编译的时候没有生成具体类型,所有找不到。

0 人点赞