目錄
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,陣列類别範本是簡易的線性表資料結構;