1 1. 上機考 20% 期末考 6/23( 四 ) 晚 6:30~8:30 範圍 : 第 7, 8, 9, 10 章實習內容 按座位坐, 隨機抽兩題 2. 紙上測驗 20% 6/21( 二 ) :9:30~11:00 課本 7-11, 13 章內容 2 第 11 章樣版 (Templates) 11.1 簡介 11.2 函式樣版 11.3 多載函式樣版 11.4 類別樣版 11.5 類別樣版與無型 (Nontype) 參數 11.6 樣版與繼承 11.7 樣版與夥伴 (Friends) 關係 11.8 樣版與靜態成員 1
3 樣版 (Templates) 11.1 簡介 函式樣版 (Function Templates) 用來建構功能相同的函式 類別樣版 (Class Templates) 用來建構功能相同的類別 4 11.2 函式樣版 多載函式與函式樣版 多載函式 具相似運算 - 不同資料型態 針對一個 signature 撰寫一個函式 函式樣版 具相同運算 - 不同資料型態 只需撰寫一個函式樣版 編譯器會產生多個不同的函式 型態檢查 2
5 11.2 函式樣版 函式樣版定義 關鍵字 :template 在 < > 括號內列出型態參數 型態參數前使用關鍵字 class 或 typename class 和 typename 可互換 template< class T > template< typename ElementType > template< class BorderType, class FillType > 可用來表示多種型態的 參數, 傳回資料, 或函式內變數 1 // Fig. 11.1: fig11_01.cpp 2 // 使用樣版 3 #include <iostream> 4 5 using std::cout; 6 using std::endl; 7 8 // 定義樣版函式 printarray 9 template< class T > // 設定一個型態 T 10 void printarray( const T *array, const int count ) 11 { 12 for ( int i = 0; i < count; i++ ) 13 cout << array[ i ] << " "; 14 15 cout << endl; 16 17 } // end function printarray 18 19 int main() 20 { 21 const int acount = 5; 22 const int bcount = 7; 23 const int ccount = 6; 24 fig11_01.cpp (1 of 2) 6 3
25 int a[ acount ] = { 1, 2, 3, 4, 5 }; 26 double b[ bcount ] = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7}; 27 char c[ ccount ] = HELLO ; // 第 6 個位置是 0 28 29 cout << "Array a contains:" << endl; 30 31 // a 是 int 陣列, 樣版函式會產生一個 T=int 的函式 32 printarray( a, acount ); 33 34 cout << "Array b contains:" << endl; 35 36 // b 是 double 陣列, 樣版函式會產生一個 T=double 的函式 37 printarray( b, bcount ); 38 39 cout << "Array c contains:" << endl; 40 41 // c 是 char 陣列, 樣版函式會產生一個 T=char 的函式 42 printarray( c, ccount ); 43 44 return 0; 45 46 } // end main fig11_01.cpp (2 of 2) 7 Array a contains: 1 2 3 4 5 Array b contains: 1.1 2.2 3.3 4.4 5.5 6.6 7.7 Array c contains: H E L L O fig11_01.cpp output (1 of 1) 8 4
9 11.3 多載函式樣版 函式樣版 產生的函式都有相同名字 編譯器使用多載方式 多載函式樣版 參數不同 可宣稱多個名稱一樣的函式樣版 可宣稱具相同名稱的非樣版函式 編譯器會執行對映程序 試著找到最符合的函式與參數型態 10 Stack 11.4 類別樣版 LIFO( 後進先出 ) 結構 針對一種資料型態必須定義一個類別 使用類別樣版 (Class templates) 設計一個通用的 stack 類別 可產生多種資料型態的 stack 類別 將資料型態參數化 需要一或多個資料型態參數 自訂 通用類別 " 樣版 5
1 // Fig. 11.2: tstack1.h 2 // Stack 類別定義 - 類別樣版 3 #ifndef TSTACK1_H 4 #define TSTACK1_H 5 6 template< class T > // 使用樣版, 定義一個型態參數 T 7 class Stack { 8 9 public: 10 Stack( int = 10 ); // 內定建構子, 設定 stack 大小 11 12 // 解構子 13 ~Stack() 14 { 15 delete [] stackptr; 16 17 } // end ~Stack destructor 18 19 bool push( const T& ); // 將一個型態為 T 的資料加入 stack 20 bool pop( T& ); // 由 stack 中取出資料型態為 T 的元素 21 22 // 檢查 stack 是不是空的 23 bool isempty() const 24 { 25 return top == -1; 26 27 } // end function isempty 28 tstack1.h (1 of 3) 11 29 // 檢查 stack 是不是滿的 30 bool isfull() const 31 { 32 return top == size - 1; 33 34 } // end function isfull 35 36 private: 37 int size; // stack 大小 38 int top; // 最上面元素的位置 39 T *stackptr; // stack 資料的指標 40 41 }; // end class Stack 42 43 // 建構子 44 template< class T > // 這裏也要寫一次 45 Stack< T >::Stack( int s )// 類別名稱改用 Stack< T > 46 { 47 size = s > 0? s : 10; 48 top = -1; // Stack 一開始沒有資料 49 stackptr = new T[ size ]; // 配置記憶體 50 51 } // end Stack constructor 52 tstack1.h (2 of 3) 12 6
53 // 將資料加入 stack 54 // 成功傳回 true; 失敗傳回 false 55 template< class T > 56 bool Stack< T >::push( const T &pushvalue ) 57 { 58 if (!isfull() ) {// 如果還沒滿 59 stackptr[ ++top ] = pushvalue; // 將資料放入 Stack 60 return true; // 成功 61 62 } // end if 63 64 return false; // 失敗 65 66 } // end function push 67 68 // 從 stack 取出一筆資料 ; 69 // 成功傳回 true; 失敗傳回 false 70 template< class T > 71 bool Stack< T >::pop( T &popvalue ) 72 { 73 if (!isempty() ) {// 如果還有資料 74 popvalue = stackptr[ top-- ]; // 取出資料 75 return true; // 成功 76 77 } // end if 78 79 return false; // 失敗 80 81 } // end function pop 82 83 #endif tstack1.h (3 of 3) 13 1 // Fig. 11.3: fig11_03.cpp 2 // Stack 類別測試程式 1 3 #include <iostream> 4 5 using std::cout; 6 using std::cin; 7 using std::endl; 8 9 #include tstack1.h // 引用 Stack 類別定義 10 11 int main() 12 { 13 Stack< double > doublestack( 5 );// 宣稱一個 double stack 物件 14 double doublevalue = 1.1; 15 16 cout << "Pushing elements onto doublestack\n"; 17 // 將資料放入 stack, 直到 stack 滿 18 while ( doublestack.push( doublevalue ) ) { 19 cout << doublevalue << ' '; 20 doublevalue += 1.1; 21 22 } // end while 23 24 cout << "\nstack is full. Cannot push " << doublevalue 25 << "\n\npopping elements from doublestack\n"; 26 // 由 stack 取出資料, 直到 stack 空 27 while ( doublestack.pop( doublevalue ) ) 28 cout << doublevalue << ' '; 29 30 cout << "\nstack is empty. Cannot pop\n"; fig11_03.cpp (1 of 2) 14 7
31 // 宣稱一個 int stack 物件 32 Stack< int > intstack; 33 int intvalue = 1; 34 cout << "\npushing elements onto intstack\n"; 35 // 將資料放入 stack, 直到 stack 滿 36 while ( intstack.push( intvalue ) ) { 37 cout << intvalue << ' '; 38 ++intvalue; 39 40 } // end while 41 42 cout << "\nstack is full. Cannot push " << intvalue 43 << "\n\npopping elements from intstack\n"; 44 // 由 stack 取出資料, 直到 stack 空 45 while ( intstack.pop( intvalue ) ) 46 cout << intvalue << ' '; 47 48 cout << "\nstack is empty. Cannot pop\n"; 49 50 return 0; 51 52 } // end main fig11_03.cpp (2 of 2) 15 Pushing elements onto doublestack 1.1 2.2 3.3 4.4 5.5 Stack is full. Cannot push 6.6 Popping elements from doublestack 5.5 4.4 3.3 2.2 1.1 Stack is empty. Cannot pop Pushing elements onto intstack 1 2 3 4 5 6 7 8 9 10 Stack is full. Cannot push 11 Popping elements from intstack 10 9 8 7 6 5 4 3 2 1 Stack is empty. Cannot pop fig11_03.cpp (3 of 3) 16 fig11_03.cpp output (1 of 1) 8
1 // Fig. 11.4: fig11_04.cpp 2 // Stack 類別測試程式二 3 // 使用函式樣版處理 Stack< T > 類別 4 #include <iostream> 5 6 using std::cout; 7 using std::cin; 8 using std::endl; 9 10 #include tstack1.h // 引用 Stack 類別樣版定義 11 12 // 定義一個函式樣版處理 Stack< T > 類別 13 template< class T > 14 void teststack( 15 Stack< T > &thestack, // Stack< T > 的 reference 16 T value, // stack 的初始值 17 T increment, // 遞增值 18 const char *stackname ) // Stack < T > 物件名稱 19 { 20 cout << "\npushing elements onto " << stackname << '\n'; 21 22 while ( thestack.push( value ) ) { 23 cout << value << ' '; 24 value += increment; 25 26 } // end while fig11_04.cpp (1 of 2) 17 27 28 cout << "\nstack is full. Cannot push " << value 29 << "\n\npopping elements from " << stackname << '\n'; 30 31 while ( thestack.pop( value ) ) 32 cout << value << ' '; 33 34 cout << "\nstack is empty. Cannot pop\n"; 35 36 } // end function teststack 37 38 int main() 39 { 40 Stack< double > doublestack( 5 ); 41 Stack< int > intstack; 42 43 teststack( doublestack, 1.1, 1.1, "doublestack" ); 44 teststack( intstack, 1, 1, "intstack" ); 45 46 return 0; 47 48 } // end main fig11_04.cpp (2 of 2) 18 9
Pushing elements onto doublestack 1.1 2.2 3.3 4.4 5.5 Stack is full. Cannot push 6.6 Popping elements from doublestack 5.5 4.4 3.3 2.2 1.1 Stack is empty. Cannot pop 19 fig11_04.cpp output (1 of 1) Pushing elements onto intstack 1 2 3 4 5 6 7 8 9 10 Stack is full. Cannot push 11 Popping elements from intstack 10 9 8 7 6 5 4 3 2 1 Stack is empty. Cannot pop 20 11.5 類別樣版與非型態參數 類別樣版 非型態參數 預設參數 被當成 const 例如 : template< class T, int elements > Stack< double, 100 > mostrecentsalesfigures; 宣稱資料型態為 Stack< double, 100> 的物件 型態參數 預設型態 例如 : template< class T = string > 10
21 11.5 類別樣版與非型態參數 多載類別樣版 特定型態的類別 不符合一般的類別樣版 例如 : template<> Class Array< Martian > { }; 22 11.6 樣版與繼承 樣版與繼承有幾種關係 類別樣版繼承一般類別 一般類別繼承類別樣版產生的類別 類別樣版繼承類別樣版產生的類別 11
23 11.7 樣版與夥伴 (friend) 函式 類別樣版的 friend 關係 全域函式 其它類別的成員函式 整個類別 24 11.7 樣版與夥伴 (friend) 函式 friend 函式 template< class T > class X friend void f1(); f1() 是所有類別樣版產生之類別的 friend friend void f2( X< T > & ); f2( X< float > &) 只是 X< float > 的 friend, f2( X< double > & ) 只是 X< double > 的 friend, f2( X< int > & ) 只是 X< int > 的 friend, 12
25 11.7 樣版與夥伴 (friend) 函式 friend 函式 template< class T > class X friend void A::f4(); 類別 A 的成員函式 f4 是所有類別樣版產生之類別的 friend friend void C< T >::f5( X< T > & ); 成員函式 C<float>::f5( X< float> & ) 只會是 X<float> 的夥伴函式 26 11.7 樣版與夥伴 (friend) 函式 friend 類別 template< class T > class X friend class Y; 類別 Y 是每一種類別的 friend friend class Z<T>; class Z<float> 只是類別 X<float> 的 friend. 13
27 11.8 樣版與靜態 (static) 成員 非樣版類別 (Non-template class) 一個類別只存在一份靜態資料成員 所有屬於同一類別的物件共用 類別樣版 所產生的每個類別有一份靜態資料成員 靜態變數必須在檔案內初始化 實作類別成員函式的檔案 14