2019年3月26日 星期二

c++ 善用 smart pointer 在物件建構法中傳回不同類型的物件並統一函式介面的作法

每個物件通常有不同的函式介面, 想要將兩種不同物件的方法統一成相同介面, 可以利用物件建構法將它封裝起來, 用這新類型物件去實例化想要的物件, 達成傳回多型物件(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( );
}

沒有留言: