【c++】 模板初阶

[复制链接]
发表于 2026-1-15 08:19:12 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
泛型编程

写一个交换函数,在学习模板之前,为了匹配差异的参数范例,我们可以使用函数重载来实现。
  1. void Swap(int& a, int& b)
  2. {
  3.         int c = a;
  4.         a = b;
  5.         b = c;
  6. }
  7. void Swap(char& a, char& b)
  8. {
  9.         char c = a;
  10.         a = b;
  11.         b = c;
  12. }
  13. void Swap(double& a, double& b)
  14. {
  15.         double c = a;
  16.         a = b;
  17.         b = c;
  18. }
  19. //...
复制代码
固然如许好像办理了标题,但是如许的操持写着太过贫苦,只要出现新范例就须要写新的函数,代码的复用率很低。有没有什么可以让我们一劳永逸呢?模板就可以实现这一功能。
这种通过抽象和模板化来编写可重用和机动的代码以此提升代码的可读性和维护性,同时克制代码重复的方式称为泛型编程。
函数模板

函数模板是c++中的一类机制,通过在函数界说中使用模板参数,我们可以编写一个函数,而在调用时根据实际参数的范例自动天生相应的版本。
  1. template <class T>
  2. void Swap(T& a, T& b)
  3. {
  4.         T c = a;
  5.         a = b;
  6.         b = c;
  7. }
复制代码
如许编译器就可以根据传入的参数范例来天生对应的Swap()函数,大大进步了代码的复用率。下面我们来实验运行一下。
  1. template <class T>
  2. void Swap(T& a, T& b)
  3. {
  4.         T c = a;
  5.         a = b;
  6.         b = c;
  7. }
  8. int main(){        int a,b;        a = 1; b = 2;        double c, d;        c = 0.0; d = 1.2;        Swap(a, b);        Swap(c, d);        cout << a << "  " << b << endl;    cout << c << "  " << d << endl;        return 0;}
复制代码

我们发现,调用Swap()之后,int范例的ab和double范例的cd都完成了交换。但是他们是否调的是同一个函数呢?
转到反汇编: 

我们发现两次调用的是差异的Swap()函数,根据传入参数范例的差异 ,编译器会天生差异的函数。然后再调用天生的函数。
函数模板的实例化

通过函数模板天生对应函数的过程叫做函数实例化。
当模板的参数只有一个时,却传入了差异范例的变量,编译器无法推导出T的范例,出现了推导错误。
  1. template <class T>
  2. void Swap(T& a, T& b)
  3. {
  4.         T c = a;
  5.         a = b;
  6.         b = c;
  7. }
  8. int main(){        int a, b;        a = 1; b = 2;        double c, d;        c = 0.0; d = 1.2;        Swap(a, c);        Swap(b, d);        cout << a << "  " << b << endl;        cout << c << "  " << d << endl;        return 0;}
复制代码
 然后我们就会发现报错了:

我们写的模板中是两个雷同的范例T,在实例化的过程中出现了推导标题不能天生对应的函数。
不重新界说模板参数的情况下,要办理这个标题有两种方法:
1.推导实例化,任然让编译器来推导出T的范例,通过逼迫范例转换来让传入的变量范例同等。
  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #include <iostream>
  3. using namespace std;
  4. template <class T>
  5. T Add(const T& a, const T& b)
  6. {
  7.         return a + b;
  8. }
  9. int main()
  10. {
  11.         int a1 = 10, a2 = 5;
  12.         double d1 = 11.2, d2 = 12.6;
  13.         cout << Add(a1, (int)d1) << endl;
  14.         cout << Add((double)a1, d1) << endl;
  15.         return 0;
  16. }
复制代码

2.表现实例化,不消编译器推导T的范例,直接指定T的范例。
  1. template <class T>
  2. T Add(const T& a, const T& b)
  3. {
  4.         return a + b;
  5. }
  6. int main()
  7. {
  8.         int a1 = 10, a2 = 5;
  9.         double d1 = 11.2, d2 = 12.6;
  10.         /*cout << Add(a1, (int)d1) << endl;
  11.         cout << Add((double)a1, d1) << endl;*/
  12.         cout << Add<int>(a1, d1) << endl;
  13.         cout << Add<double>(a1, d1) << endl;
  14.         return 0;
  15. }
复制代码

这两种方法都可以办理推导标题,但是都对精度有影响。 而且当T不作参数时,只能使用表现实例化。
模板函数的匹配原则

当函数模板和现成的函数同时存在时,编译器会选择现成的函数。很简朴,有现成的为什么还要本身天生呢。
  1. T Add(const T& a, const T& b)
  2. {
  3.         return a + b;
  4. }
  5. int Add(int a,int b)
  6. {
  7.         return (a + b) * 10;
  8. }
  9. int main()
  10. {
  11.         int a = 1;
  12.         int b = 2;
  13.         cout << Add(a, b) << endl;
  14.        
  15.         return 0;
  16. }
复制代码

类模板 
  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #include <iostream>
  3. using namespace std;
  4. template <typename T>
  5. class Stack
  6. {
  7. public:
  8.         Stack(int n = 4)
  9.                 :_array(new T[n])
  10.                 , _capacity(n)
  11.                 , _size(0)
  12.         {
  13.         }
  14.         void push(const T& x)
  15.         {
  16.                 if (_capacity == _size)
  17.                 {
  18.                         T* tmp= new T[2 * _capacity];
  19.                         memcpy(tmp, _array, _size * sizeof(T));
  20.                         delete[] _array;
  21.                         _array = tmp;
  22.                         _capacity *= 2;
  23.                 }
  24.                 _array[_size++] = x;
  25.         }
  26.         ~Stack()
  27.         {
  28.                 delete[] _array;
  29.                 _array = nullptr;
  30.                 _capacity = _size = 0;
  31.         }
  32. private:
  33.         T * _array;
  34.         int _capacity;
  35.         int _size;
  36. };
  37. int main()
  38. {
  39.         //类模板都是显示实例化
  40.         Stack<int> str1;
  41.         str1.push(1);
  42.         str1.push(2);
  43.         str1.push(3);
  44.         return 0;
  45. }
复制代码
底层: 
 
起首,类模板不能推导实例化。
 编译器不能自动推导出类中T的范例,这点和T作返回值不作参数的情况一样,编译器没有推理此中T范例的依据,以是不手动规定类的范例,就会报错。先比于c语言,用类模板的类可以储存差异范例的数据而不消重新在写一个Stack。


当界说和声明分离时,须要重新声明模板,而且99%的情况下,不能把界说和声明放到两个文件中。 
  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #include <iostream>
  3. using namespace std;
  4. template <typename T>
  5. class Stack
  6. {
  7. public:
  8.         Stack(int n = 4)
  9.                 :_array(new T[n])
  10.                 , _capacity(n)
  11.                 , _size(0)
  12.         {
  13.         }
  14.         void push(const T& x);
  15.        
  16.         ~Stack()
  17.         {
  18.                 delete[] _array;
  19.                 _array = nullptr;
  20.                 _capacity = _size = 0;
  21.         }
  22. private:
  23.         T * _array;
  24.         int _capacity;
  25.         int _size;
  26. };
  27. template <typename T>//重新声明模板
  28.    void Stack<T>::push(const T& x)
  29. {
  30.         if (_capacity == _size)
  31.         {
  32.                 T* tmp = new T[2 * _capacity];
  33.                 memcpy(tmp, _array, _size * sizeof(T));
  34.                 delete[] _array;
  35.                 _array = tmp;
  36.                 _capacity *= 2;
  37.         }
  38.         _array[_size++] = x;
  39. }
  40. int main()
  41. {
  42.         //类模板都是显示实例化
  43.         Stack<int> str1;
  44.         str1.push(1);
  45.         str1.push(2);
  46.         str1.push(3);
  47.         return 0;
  48. }
复制代码
 

回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表