目录
1、初识数值型模板参数
在泛型编程中,数据的值和类型都被参数化。在第二次编译时,编译器会根据模板中的类型参数<实参.>去推导形参的值与类型;也就是说,模板不仅支持值的传递,还支持类型的传递,这就是模板与普通函数的最大区别了。
模板参数可以是数字型参数,也可以是类型参数;接下来我们以代码来说明什么是数值型模板参数?
1 template <typename T, int N>
2 void func()
3 {
4 T a[N]; // 使用模板参数定义局部数组;
5 }
6
7 func<double, 10>(); // 使用模板时,数值型参数必须是常量,不能是变量;
代码中 N 就是模板中的数值型参数,当发生函数调用时,这个数值型参数就被初始化为常量 10;
那么什么才是合法的数值型模板参数呢?
1. 在发生调用时,变量不能作为数值型模板参数;(在编译时,变量的值还没有唯一确定;如 int a = 10; func<double, a>(); 编译失败)
2. 在模板声明时,浮点数不能作为数值型模板参数;(浮点数本身就不精确;如 template <typename T, double N> 编译失败 )
3. 类对象不能作为数值型模板参数;(类对象的属性包含了1、2 这两点)
....
总之,合法的数值型模板参数必须满足,在编译阶段确保数值型模板参数是唯一确定的。
2、数值型模板参数的应用
1. 用你觉得最高效的方法求 1 + 2 + 3 + ... + N 的值;
求前N项和的方法有很多,比如 for 循环求取(栈空间、堆空间)、等差数列求和(公式法)、递归求取等等。但是题中明确指定是最高效的求和方法,怎么做呢?接下来我们分别分析这三种方法:
(1)如果用 for循环、递归求取,会随着项数N的增多,导致程序的执行步骤也会增加;显然这时候公式法就比较合适了;(程序执行的步骤是判断算法执行效率的方式之一)
(2)但是,如何使求和算法做到最高效呢?试想我们能不能站在内存的角度考虑,如果能将这前N项和的结果直接存储在内存中,当我们需要时,直接从内存中读取是不是会更高效呢。
有了上面的铺垫,现在我们就带着这种思想去实现这个算法(涉及内容:类模板的完全特化、模板的数值型参数、static 和 const 关键字、递归算法)。
1 #include <iostream>
2 #include <string>
3
4 using namespace std;
5
6 template
7 < int N >
8 class Sum
9 {
10 public:
11 static const int VALUE = Sum<N-1>::VALUE + N; // 递归思想:如果知道了前(N-1)项的结果,再加上第N项的结果就可以求出前N项和的结果
12 };
13
14 /* 定义上述类模板的完全特化实现,实现递归出口 N = 1 */
15 template
16 < >
17 class Sum < 1 >
18 {
19 public:
20 static const int VALUE = 1;
21 };
22
23 /**
24 * static const int VALUE = 1;
25 * 1 用字面量去初始化const常量,编译器会将该常量放入符号表中,当使用该常量时,再从符号表中取出该常量的值;
26 * 2 static 会把所修饰的变量存储到全局数据区,方便让所有对象共享;
27 * 3 所以,static const 的组合使用会将符号表放入到全局数据区;
28 */
29
30 int main()
31 {
32 cout << "1 + 2 + 3 + ... + 10 = " << Sum<10>::VALUE << endl;
33 cout << "1 + 2 + 3 + ... + 100 = " << Sum<100>::VALUE << endl;
34
35 return 0;
36 }
37
38 /**
39 * 运行结果:
40 * 1 + 2 + 3 + ... + 10 = 55
41 * 1 + 2 + 3 + ... + 100 = 5050
42 */
最高效的求和算法(递归+类模板完全特化)
通过这个案列,我们要学会举一反三,从而写出更高效的程序。
2. 数组模板类的实现
(1)栈空间创建数组模板类
1 // array.hpp 数组模板类文件
2 #ifndef ARRAY_H
3 #define ARRAY_H
4
5 #include <iostream>
6
7 template
8 < typename T, int N > // 数组元素的类型和大小;
9 class Array
10 {
11 T m_array[N]; // 定义一个实际的数组;
12 public:
13 int length();
14 bool set(int index, T value);
15 bool get(int index, T& value);
16 T& operator[] (int index);
17 T operator[] (int index) const; // 数组类对象有可能是 const 对象,这个时候就只能调用 const 成员函数,所以要定义这个;const 函数只能返回值,不能返回引用;
18 void print();
19 void sort();
20 virtual ~Array(); // 有可能被继承
21 };
22
23 template
24 < typename T, int N >
25 int Array<T, N>::length()
26 {
27 return N;
28 }
29
30 template
31 < typename T, int N >
32 bool Array<T, N>::set(int index, T value)
33 {
34 bool ret = (0 <= index) && (index < N);
35
36 if( ret )
37 {
38 m_array[index] = value;
39 }
40
41 return ret;
42 }
43
44 template
45 < typename T, int N >
46 bool Array<T, N>::get(int index, T& value)
47 {
48 bool ret = (0 <= index) && (index < N);
49
50 if( ret )
51 {
52 value = m_array[index];
53 }
54
55 return ret;
56 }
57
58 template
59 < typename T, int N >
60 T& Array<T, N>::operator[] (int index)
61 {
62 return m_array[index];
63 }
64
65 template
66 < typename T, int N >
67 T Array<T, N>::operator[] (int index) const
68 {
69 return m_array[index];
70 }
71
72 template
73 < typename T, int N >
74 void Array<T, N>::print()
75 {
76 for(int i=0; i< N; i++)
77 {
78 std::cout << m_array[i] << " ";
79 }
80
81 std::cout << std::endl;
82 }
83
84 template
85 < typename T >
86 void Swap(T& a, T& b)
87 {
88 T c = a;
89 a = b;
90 b = c;
91 }
92
93 template
94 < typename T, int N >
95 void Array<T, N>::sort()
96 {
97 for(int i=0; i<N; i++)
98 {
99 for(int j=i; j<N; j++)
100 {
101 if( m_array[i] > m_array[j] )
102 {
103 Swap(m_array[i], m_array[j]);
104 }
105 }
106 }
107 }
108
109 template
110 < typename T, int N >
111 Array<T, N>::~Array()
112 {
113
114 }
115
116 #endif
117
118 // main.cpp 测试文件
119
120 #include <iostream>
121 #include <string>
122 #include "array.hpp"
123
124 using namespace std;
125
126 int main()
127 {
128 Array<double, 5> ad; // 相当于 double[5]
129
130 for(int i=0; i<ad.length(); i++)
131 {
132 ad[i] = 100 - i * 0.5;
133 }
134
135 ad.print(); // 100 99.5 99 98.5 98
136 ad.sort(); // 升序排列
137 ad.print(); // 98 98.5 99 99.5 100
138
139 Array<int, 5> ai; // 相当于 int[5]
140
141 for(int i=0; i<ai.length(); i++)
142 {
143 ai[i] = i * i;
144 }
145
146 ai.print(); // 0 1 4 9 16
147
148 return 0;
149 }
栈空间中的数组模板类实现
(2)堆空间创建数组模板类
1 // heapArray.hpp 数组模板类文件
2 #ifndef HEAPARRAY_H
3 #define HEAPARRAY_H
4
5 template
6 < typename T, int N > // 数组元素的类型和大小;
7 class HeapArray
8 {
9 private:
10 T* m_pointer;
11
12 HeapArray();
13 HeapArray(const HeapArray<T, N>& obj);
14 bool construct();
15 public:
16 static HeapArray<T, N>* NewInstance();
17 int length();
18 bool get(int index, T& value);
19 bool set(int index ,T value);
20 T& operator [] (int index);
21 T operator [] (int index) const; // 有可能有 const 对象;
22 HeapArray<T, N>& self();
23 ~HeapArray(); // 这个时候构造函数是 private 的,也就是 HeapArray 类不希望被继承,所以说没有必要将它声明为 virtual 的;
24 };
25
26 /* 声明与实现要在同一个文件中 */
27
28 template
29 < typename T, int N >
30 HeapArray<T, N>::HeapArray()
31 {
32 // 与资源无关的操作
33 }
34
35 template
36 < typename T, int N >
37 bool HeapArray<T, N>::construct()
38 {
39 m_pointer = new T[N]; // 申请内存
40
41 return m_pointer != 0;
42 }
43
44 template
45 < typename T, int N >
46 HeapArray<T, N>* HeapArray<T, N>::NewInstance()
47 {
48 HeapArray<T, N>* ret = new HeapArray<T, N>();
49
50 if( !(ret && ret->construct()) )
51 {
52 delete ret;
53 ret = 0;
54 }
55
56 return ret;
57 }
58
59 template
60 < typename T, int N >
61 int HeapArray<T, N>::length()
62 {
63 return N;
64 }
65
66 template
67 < typename T, int N >
68 bool HeapArray<T, N>::get(int index, T& value)
69 {
70 bool ret = (0 <= index) && (index < N);
71
72 if( ret )
73 {
74 value = m_pointer[index];
75 }
76
77 return ret;
78 }
79
80 template
81 < typename T, int N >
82 bool HeapArray<T, N>::set(int index, T value)
83 {
84 bool ret = (0 <= index) && (index < N);
85
86 if( ret )
87 {
88 m_pointer[index] = value;
89 }
90
91 return ret;
92 }
93
94 template
95 < typename T, int N >
96 T& HeapArray<T, N>::operator [] (int index)
97 {
98 return m_pointer[index];
99 }
100
101 template
102 < typename T, int N >
103 T HeapArray<T, N>::operator [] (int index) const
104 {
105 return m_pointer[index];
106 }
107
108 template
109 < typename T, int N >
110 HeapArray<T, N>& HeapArray<T, N>::self()
111 {
112 return *this;
113 }
114
115 template
116 < typename T, int N >
117 HeapArray<T, N>::~HeapArray()
118 {
119 delete[]m_pointer;
120 }
121
122 #endif
123
124 // main.cpp 测试文件
125
126 #include <iostream>
127 #include <string>
128 #include "heapArray.hpp"
129
130 using namespace std;
131
132 int main()
133 {
134 HeapArray<char, 10>* pac = HeapArray<char, 10>::NewInstance(); // 在堆区申请 10 char
135
136 if( pac != NULL )
137 {
138 HeapArray<char, 10>& ac = pac->self();
139
140 for(int i=0; i<ac.length(); i++)
141 {
142 ac[i] = i + 'a';
143 }
144
145 for(int i=0; i<ac.length(); i++)
146 {
147 cout << ac[i] << " ";
148 }
149
150 cout << endl;
151 }
152
153 delete pac;
154
155
156 HeapArray<int, 10>* pai = HeapArray<int, 10>::NewInstance(); // 在堆区申请 10 int
157
158 if( pai != NULL )
159 {
160 HeapArray<int, 10>& ai = pai->self();
161
162 for(int i=0; i<ai.length(); i++)
163 {
164 ai[i] = i + 1;
165 }
166
167 for(int i=0; i<ai.length(); i++)
168 {
169 cout << ai[i] << " ";
170 }
171
172 cout << endl;
173 }
174
175 delete pai;
176
177 return 0;
178 }
179 /**
180 * 运行结果:
181 * a b c d e f g h i j
182 * 1 2 3 4 5 6 7 8 9 10
183 */
堆空间中的数组模板类实现
本节总结:
1,模板参数可以是数值型参数;
2,数值型模板参数必须在编译期间唯一确定;
3,数组类模板是基于数值型模板参数实现的;
4,数组类模板是简易的线性表数据结构;