數值型模板引數的應用

PRO_Z發表於2021-07-18

目錄

  1. 初識數值型模板引數

  2. 數值型模板引數的應用

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

 

相關文章