数值型模板参数的应用
目录
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,数组类模板是简易的线性表数据结构;