每個物件通常有不同的函式介面, 想要將兩種不同物件的方法統一成相同介面, 可以利用物件建構法將它封裝起來, 用這新類型物件去實例化想要的物件, 達成傳回多型物件(polymorphic class)的目的:
#include <stdio.h>
struct base { // 基礎類型
virtual void work( ){ }//擬統一的函式介面
virtual ~base(){ } // 避免繼承者,當在解構式中想刪除指標物件時,編譯器會報出錯務訊息
}; // 先建構基礎物件抽象類型, 統一函式介面
struct A: base { // 繼承 base 物件, 將物件用物件 A 封裝起來
void work( ) override { printf("A\n"); } // 將方法封裝並實現函式介面
};
struct B: base {// 繼承 base 物件, 將另一物件用物件 B 封裝起來
void work( ) override { printf("B\n"); } // 同樣將方法封裝並實現函式
};
struct C { // 一個全新多型類型(polymorphic class)
base *ptr;// 基礎物件的指標, 可以封裝物件 A 或 B
~C( ) { delete ptr; }
C(int a) { // 根據輸入值實例化不同物件, 奇數傳回 A, 偶數傳回 B, 等同於傳回不同物件
if (a%2) ptr = (base *) new A(); // 實例化物件 A 指標
else ptr = (base *) new B(); // 實例化物件 B 指標
}
void work( ){ ptr->work( ); }// 將多型物件成員用指標連結函式介面,方便使用
};
int main(){
for(int i=0; i < 10; i++) C(i).work( );
}
如果害怕指標的用法(像是造成 memory leak or double free), 可以用 smart pointer 將它封裝起來, 讓系統自動處理, 就不用擔心記憶體釋放的問題. c++ 所提供的 std::shared_ptr < > 就是 smart pointer 其中之一用來封裝類型指標的標準樣板函式(STL), 但有一點要注意的是, 用 smart pointer 封裝類型時, 該類型的建構及解構方法必須放在 public: 區, 讓 STL 可以正常呼叫, 否則會出現一堆錯誤訊息. 又或者很不喜歡用指標箭頭的方法, 還可以用左值(&alias) 換化成正常物件, 將方法從指標箭頭轉成正常物件的使用方式.
#include <stdio.h>
#include <memory> // smart pointer 要使用 memory 函式庫
typedef std::shared_ptr <base> baseptr; // 封裝成 smart pointer, 讓系統自動管理
struct base { // 基礎類型
virtual void work( ){}
virtual ~base(){ } // 避免繼承者若在解構式中想刪除指標物件時會報出錯務訊息
}; // 先建構基礎物件的抽象類型, 統一函式介面
struct A: base { // 繼承 base 類型, 可以將物件用類型 A 封裝起來
void work( ) override { printf("A\n"); } // 將方法封裝並覆蓋虛擬函式
};
struct B: base {// 繼承 base 類型 , 將另一物件用類型 B 封裝起來
void work( ) override { printf("B\n"); } // 同樣將方法封裝並覆蓋虛擬函式
};
struct C { // 一個全新多型類型(polymorphic class)
baseptr ptr;// 宣告 ptr 成為 smart pointer 可以封裝物件 A 或 B 類型的指標
// 無需解構式 ~C( ){ }
C(int a) { // 根據輸入值實例化不同物件, 奇數傳回 A, 偶數傳回 B, 等同傳回不同物件
if (a%2) ptr = baseptr(new A( )); // 實例化物件 A 指標, 同時轉換成 smart pointer
else ptr = baseptr(new B( )); // 實例化物件 B 指標, 同時轉換成 smart pointer
}
};
int main(){ // 呼叫時, 先用物件直取多型成員, 接著就能用指標 -> 呼叫函式介面
for(int i=0; i < 10; i++) C(i).ptr->work( );
}
沒有留言:
張貼留言