【C++】-- 模板详解(二)

简介: 【C++】-- 模板详解

三、类模板

1.类模板定义

当有多个类时,其功能相同,仅仅数据类型不同,就可以使用类模板

1. template <class T1, class T2, ……, class Tn>
2. class 类模板名
3. {
4.  //类内成员定义
5. };

例如对于链表,以下代码只能插入整型结点,因为限定了VDataType的类型为int型:

1. #include<iostream>
2. #include<assert.h>
3. using namespace std;
4. 
5. typedef int VDataType;//限定了VDataType的类型只能为int型
6. class vector
7. {
8. public:
9.     //构造函数
10.   vector()
11.     :_a(nullptr)
12.     , _size(0)
13.     , _capacity(0)
14. 
15.   {}
16.     
17.     //析构函数
18.   ~vector()
19.   {
20.     delete[] _a;
21.     _a = nullptr;
22.     _size = _capacity = 0;
23.   }
24.     
25.     //读写第pos个位置的元素
26.   int& operator[](size_t pos)
27.   {
28.     assert(pos < _size);
29.     return _a[pos];
30.   }
31. 
32.   //求数组大小
33.   size_t size()
34.   {
35.     return _size;
36.   }
37. 
38.     //插入节点
39.   void push_back(const int& x)
40.   {
41.     if (_size == _capacity)
42.     {
43.       int newCapacty = _capacity == 0 ? 4 : _capacity * 2;
44.       int* tmp = new int[newCapacty];
45.       if (_a)
46.       {
47.         memcpy(tmp, _a, sizeof(int) * _size);
48.         delete[] _a;
49.       }
50.       _a = tmp;
51.       _capacity = newCapacty;
52.     }
53. 
54.     _a[_size] = x;
55.     ++_size;
56.   }
57. 
58. private:
59.   VDataType* _a;
60.   int _size;
61.   int _capacity;
62. };

但是现在如果想借助这一份代码,让链表v1存int型数据,让另一个链表v2存double型数据,该怎样实现呢?使用类模板:

1. #include<iostream>
2. #include<assert.h>
3. using namespace std;
4. 
5. template<class T>//类模板定义
6. class vector
7. {
8. public:
9.     //构造函数
10.   vector()
11.     :_a(nullptr)
12.     , _size(0)
13.     , _capacity(0)
14. 
15.   {}
16.     
17.     //析构函数
18.   ~vector()
19.   {
20.     delete[] _a;
21.     _a = nullptr;
22.     _size = _capacity = 0;
23.   }
24.     
25.     //读写第pos个位置的元素
26.   T& operator[](size_t pos)
27.   {
28.     assert(pos < _size);
29.     return _a[pos];
30.   }
31. 
32.   //求数组大小
33.   size_t size()
34.   {
35.     return _size;
36.   }
37.     
38.     //插入节点
39.   void push_back(const T& x) //指定参数类型
40.   {
41.     if (_size == _capacity)
42.     {
43.       int newCapacty = _capacity == 0 ? 4 : _capacity * 2;
44.       T* tmp = new T[newCapacty];//指定tmp类型,以便后面赋值给_a
45.       if (_a)
46.       {
47.         memcpy(tmp, _a, sizeof(int) * _size);
48.         delete[] _a;
49.       }
50.       _a = tmp;
51.       _capacity = newCapacty;
52.     }
53. 
54.     _a[_size] = x;
55.     ++_size;
56.   }
57. 
58. private:
59.   T* _a; //指定成员变量类型
60.   int _size;
61.   int _capacity;
62. };

vector不是具体的类,是编译器根据被实例化的类型生成具体类的模具。以上仅仅只是类模板的显式指定,如何向v1中插入int型数据,向v2中插入double型数据呢?

1. int main()
2. {
3.  delia::vector<int> v1; //v1存放int型数据,生成T是int型的vector
4.  v1.push_back(1);
5.  v1.push_back(2);
6. 
7.  //v1[i]等价于v1.operator()[i]
8.  for (size_t i = 0; i < v1.size(); i++)
9.  {
10.     cout << v1[i] << " ";
11.   }
12.   cout << endl;
13. 
14.   for (size_t i = 0; i < v1.size(); i++)
15.   {
16.     v1[i] *= 2;
17.     cout << v1[i] << " ";
18.   }
19.   cout << endl;
20. 
21.   delia::vector<double> v2; //v2存放double型数据,生成T是double型的vector
22.   v2.push_back(1.1);
23.   v2.push_back(2.2);
24. 
25.   return 0;
26. }

由于库里面有vector头文件,假如在库里包含了#include的头文件,编译器就不知道到底要调库里的vector还是我们自己写的vector。把库里的vector放在std命名空间中,把自己写的vector放在另一个命名空间中,就能把两个vector隔离开了:

1. #include<iostream>
2. #include<vector>
3. #include<assert.h>
4. using namespace std;
5. 
6. namespace delia
7. {
8.  template<class T>//类模板定义
9.  class vector
10.   {
11.   public:
12.     //构造函数
13.     vector()
14.       :_a(nullptr)
15.       , _size(0)
16.       , _capacity(0)
17. 
18.     {}
19. 
20.     //析构函数
21.     ~vector()
22.     {
23.       delete[] _a;
24.       _a = nullptr;
25.       _size = _capacity = 0;
26.     }
27.       
28.         //读写第pos个位置的元素
29.     T& operator[](size_t pos)
30.     {
31.       assert(pos < _size);
32.       return _a[pos];
33.     }
34. 
35.     //求数组大小
36.     size_t size()
37.     {
38.       return _size;
39.     }
40. 
41.     //插入节点
42.     void push_back(const T& x) //指定参数类型
43.     {
44.       if (_size == _capacity)
45.       {
46.         int newCapacty = _capacity == 0 ? 4 : _capacity * 2;
47.         T* tmp = new T[newCapacty];//指定tmp类型,以便后面赋值给_a
48.         if (_a)
49.         {
50.           memcpy(tmp, _a, sizeof(int) * _size);
51.           delete[] _a;
52.         }
53.         _a = tmp;
54.         _capacity = newCapacty;
55.       }
56. 
57.       _a[_size] = x;
58.       ++_size;
59.     }
60. 
61.   private:
62.     T* _a; //指定成员变量类型
63.     int _size;
64.     int _capacity;
65.   };
66. }

现在可以打印某个位置的数据了:

1. int main()
2. {
3.  delia::vector<int> v1; //v1存放int型数据,生成T是int型的vector
4.  v1.push_back(1);
5.  v1.push_back(2);
6. 
7.  //v1[i]等价于v1.operator()[i]
8.  for (size_t i = 0; i < v1.size(); i++)
9.  {
10.     cout << v1[i] << " ";
11.   }
12.   cout << endl;
13. 
14.   for (size_t i = 0; i < v1.size(); i++)
15.   {
16.     v1[i] *= 2;
17.     cout << v1[i] << " ";
18.   }
19.   cout << endl;
20. 
21.   delia::vector<double> v2; //v2存放double型数据,生成T是double型的vector
22.   v2.push_back(1.1);
23.   v2.push_back(2.2);
24. 
25.   return 0;
26. }

 

如果函数声明和定义分开,比如在类里面声明函数,但是在类外面定义函数,需要在函数定义的位置再定义一个模板参数,让编译器知道T是一个模板类型,否则编译器识别不了T是从哪里来的,是什么:

1. #include<iostream>
2. #include<vector>
3. #include<assert.h>
4. using namespace std;
5. 
6. namespace delia
7. {
8.  template<class T>//类模板定义
9.  class vector
10.   {
11.   public:
12.     //构造函数
13.     vector()
14.       :_a(nullptr)
15.       , _size(0)
16.       , _capacity(0)
17. 
18.     {}
19. 
20.     //析构函数
21.     ~vector()
22.     {
23.       delete[] _a;
24.       _a = nullptr;
25.       _size = _capacity = 0;
26.     }
27. 
28.     //类里面,读写第pos个位置的元素:函数声明
29.     T& operator[](size_t pos); 
30. 
31.     //求数组大小
32.     size_t size()
33.     {
34.       return _size;
35.     }
36. 
37.     //插入节点
38.     void push_back(const T& x) //指定参数类型
39.     {
40.       if (_size == _capacity)
41.       {
42.         int newCapacty = _capacity == 0 ? 4 : _capacity * 2;
43.         T* tmp = new T[newCapacty];
44.         if (_a)
45.         {
46.           memcpy(tmp, _a, sizeof(int) * _size);
47.           delete[] _a;
48.         }
49.         _a = tmp;
50.         _capacity = newCapacty;
51.       }
52. 
53.       _a[_size] = x;
54.       ++_size;
55.     }
56. 
57.   private:
58.     T* _a; //指定成员变量类型
59.     int _size;
60.     int _capacity;
61.   };
62. 
63.   //类外面,读写第pos个位置的元素:函数定义
64.   template<class T>//用模板类型指定了具体类型
65.   T& vector<T>:: operator[](size_t pos)
66.   {
67.     assert(pos < _size);
68.     return _a[pos];
69.   }
70. }

2.类模板的实例化

类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<> 中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。

 

delia::vector<int> v1; //vector是类模板名字,vector<int>才是类型
相关文章
|
1天前
|
算法 安全 编译器
【C++进阶】模板进阶与仿函数:C++编程中的泛型与函数式编程思想
【C++进阶】模板进阶与仿函数:C++编程中的泛型与函数式编程思想
|
1天前
|
JavaScript 前端开发 编译器
【C++初阶】C++模板编程入门:探索泛型编程的奥秘
【C++初阶】C++模板编程入门:探索泛型编程的奥秘
|
2天前
|
编译器 C++ 容器
【C++语言】模板(内附精美思维导图)
【C++语言】模板(内附精美思维导图)
|
12天前
|
存储 编译器 C++
6.C++模板(超全)
6.C++模板(超全)
|
18天前
|
编译器 C语言 C++
从C语言到C++_21(模板进阶+array)+相关笔试题(下)
从C语言到C++_21(模板进阶+array)+相关笔试题
24 2
|
18天前
|
编译器 C语言 C++
从C语言到C++_21(模板进阶+array)+相关笔试题(上)
从C语言到C++_21(模板进阶+array)+相关笔试题
24 0
|
22天前
|
存储 算法 C++
高效利用C++ STL库:标准模板库的使用技巧
本文介绍了C++ STL(标准模板库)的高效使用技巧,包括选择合适的容器类型、使用`emplace_back`而非`push_back`、预分配容器空间和范围for循环遍历容器。此外,还讨论了STL算法的运用,如用算法替代手动循环、使用lambda表达式和进行容器操作。通过这些技巧,开发者可以提升C++代码的性能和可读性。
|
22天前
|
程序员 编译器 C++
C++中的模板与泛型编程技术深度解析
C++中的模板与泛型编程技术深度解析
|
23天前
|
存储 算法 程序员
C++模板编程与泛型技术探秘
这篇文章探讨了C++中的模板编程和泛型技术,这两种技术增强了代码复用和抽象能力。文章介绍了函数模板和类模板的概念,通过示例展示了如何定义和使用它们。泛型技术是一种编程范式,强调编写与类型无关的代码,提高代码复用性和灵活性。C++11后的版本通过类型萃取和变长模板参数进一步扩展了模板功能。模板和泛型广泛应用在数据结构、算法、库和框架的开发中,如STL。掌握这些技术有助于编写更高效、灵活的代码,并推动软件开发的创新和进步。
|
25天前
|
编译器 C++
【C++】模板进阶 -- 详解
【C++】模板进阶 -- 详解