个人主页:平行线也会相交💪
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 平行线也会相交 原创
收录于专栏【C++之路】💌
本专栏旨在记录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;
}
上述代码中,我们可以对int类型、double类型、char类型进行交换,在未来,我们如果对更多类型的变量进行交换应该怎么呢?
这个是问题刚好可以利用泛型参数来解决,请看;
//模板
//函数模板+类
template<typename T>
void Swap(T& x1, T& x2)
{
T tmp = x1;
x1 = x2;
x2 = tmp;
}
int main()
{
int a = 10, b = 20;
double c = 1.1, d = 2.2;
Swap(a, b);
Swap(c, d);
return 0;
}
模板参数定义的是函数的参数类型。
另外一点,Swap(a, b);和Swap(c, d);调用的并不是同1个函数
,我们观察一下汇编代码来看一下:
如果我们想要交换两个指针,也是可以的,请看:
int* p1 = &a;int* p2 = &b;swap
,如下图我们可以看到也可以交换指针:
二、 函数模板
模板概念:
函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
2.1函数模板示例化
函数模板格式:
//返回值类型 函数名(参数列表){}
template<typename T>
void Swap( T& left, T& right)
{
T temp = left;
left = right;
right = temp;
}
上述代码是交换函数的一个代码,那我们调用的时候并不是调用的那个模板,编译器调用的其实是函数模板实例化生成的具体函数。
编译器通过模板生成具体函数的过程称之为函数模板的实例化。请看图:
在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此。
template<typename T>
void Swap(T& x1, T& x2)
{
T tmp = x1;
x1 = x2;
x2 = tmp;
}
下面举多个参数类型的例子,请看:
template<typename T1,typename T2>
T1 Func(const T1& x, const T2& y)
{
cout << x << " " << y << endl;
return x;
}
//T1 T2可以是任意类型,比如自定义类型或者内置类型
//函数模板实例化生成具体函数
//函数模板根据调用会自己推导模板参数的类型,实例化出对应的函数
int main()
{
Func(1, 2);
Func(1.1, 2.2);
return 0;
}
注意:template<typename T1>
中的typename
可以换成class
,但是不可以换成struct
。
2.2函数模板实例化的两种方式
函数模板的实例化的两种方式,分为:隐式实例化和显式实例化。
。
请注意看T Add(const T& left, const T& right)
中为什么要加const
,这里涉及到权限的问题。举个例子:cout << Add(a1, (int)d1) << endl;(隐式类型转换)
, cout << Add<double>(a1, d1) << endl; (显式类型转换)
无论是显式类型的转换还是显式类型的转换,类型的转换会产生临时变量,临时变量具有常属性,常属性不能传给普通的引用(因为权限被放大了),所以要加上const
。
我能不能这样写呢?请看:
现在我们应该怎么解决呢?首先这里解决方法有两种。
方式一(两者之间只能听其中一个的话),实参传递的时候推演T的类型:
方式二(显式实例化):
上述的显式示例化其实并不是这样完的,下面来看一下必须要用到显式示例化的场景。
三、类模板
3.1类模板的实例化
类模板的定义格式:
template<class T1, class T2, ..., class Tn>
class 类模板名
{
// 类内成员定义
};
类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类
。
template<class T>
class Stack
{
public:
Stack(size_t capacity = 10)
{
/*_array = (T*)malloc(capacity * sizeof(T));
if (nullptr == _array)
{
perror("malloc申请空间失败");
return;
}*/
_array = new T[capacity];
_size = 0;
_capacity = capacity;
}
void Push(const T& data)
{
// CheckCapacity();
_array[_size] = data;
_size++;
}
~Stack()
{
if (_array)
{
free(_array);
_array = nullptr;
_capacity = 0;
_size = 0;
}
}
private:
T* _array;
size_t _size;
size_t _capacity;
};
int main()
{
Stack<int> s1;//s1存int类型
Stack<double> s2;//s2存double类型
Stack<char> s3;//s3存char类型
return 0;
}
3.2类模板的声明和定义分离
对于普通类而言,类名就是类型。
对于类模板而言,类名是类名,类型是类型,二者是不一样的。
请看下面举例:
template<class T>
class Stack
{
public:
Stack(size_t capacity = 10);
void Push(const T& data);
~Stack();
private:
T* _array;
size_t _size;
size_t _capacity;
};
template<class T>
Stack<T>::Stack(size_t capacity)
{
/*_array = (T*)malloc(capacity * sizeof(T));
if (nullptr == _array)
{
perror("malloc申请空间失败");
return;
}*/
_array = new T[capacity];
_size = 0;
_capacity = capacity;
}
template<class T>
void Stack<T>:: Push(const T& data)
{
// CheckCapacity();
_array[_size] = data;
_size++;
}
template<class T>
Stack<T>::~Stack()
{
if (_array)
{
free(_array);
_array = nullptr;
_capacity = 0;
_size = 0;
}
}
对于上述代码而言,Stack是类名,Stack<T>是类型,但是如果类模板实例化时候,类名就是具体的Stack<int>或者Stack<char>等
。
关于类模板的声明和定义分离的讲解,在模板进阶那一部分进行更深一步的讲解。文章来源:https://www.toymoban.com/news/detail-476238.html
好了,以上就是【C++】模板初阶内容的讲解,就到这里啦!
再见啦,友友们!!!
文章来源地址https://www.toymoban.com/news/detail-476238.html
到了这里,关于【C++】---模板初阶(超详练气篇)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!