インテリジェントポインタとハンドルクラス(4)

11881 ワード

コンテナを使用して継承システム内のオブジェクトを保存する場合、コンテナが継承システムに使用するタイプに影響します.派生クラスオブジェクトがベースクラスオブジェクトにコピーされると、派生クラスオブジェクトが切り取られます.この問題を解決する方法は、通常、プログラム実行時に動的に割り当てられた派生クラスオブジェクトを指すコンテナを使用してベースクラスオブジェクトを保存するポインタであり、ユーザは、次の例のように、コンテナプロファイルの前にdeleteを呼び出して動的に割り当てられたオブジェクトを解放することを保証しなければならない.
 1 class Base  2 {  3 public:  4     virtual void action() = 0;  5 };  6 class Derived1 : public Base  7 {...};  8 class Derived2 : public Base  9 {...}; 10 class Derived3 : public Base 11 {...}; 12 
13 void fun() 14 { 15     vector<Base*> v; 16 
17     v.push_back(new Derived1); 18     v.push_back(new Derived2); 19     v.push_back(new Derived3); 20 
21     for(int i=0; i!=v.size(); i++) 22         v[i]->action(); 23         
24     for(int i=0; i!=v.size(); i++) 25  delete v[i]; 26     
27     return; 28 }

コンテナに以前定義したHandleクラスオブジェクトを保存し、Handleクラスオブジェクトは継承階層のオブジェクトポインタを管理し、終了時に動的に割り当てられたメモリを手動で解放することなくライフサイクルを制御できます.
1 vector<Handle<Base> > vh; 2 vh.push_back(new Derived1); 3 vh.push_back(new Derived2); 4 vh.push_back(new Derived3); 5 
6 for(int i=0; i!=vh.size(); i++) 7     vh[i]->action();

「C++Primer」では、コンテナクラスはハンドルクラスを保存し、ハンドルクラスは前に定義したHandleクラスオブジェクトをカプセル化し、Handleクラスオブジェクトによってポインタの管理とカウントを行い、コピー、付与、およびポインタの解放を制御します.<の継承階層を例に挙げます.
 1 class Item_Base  2 {  3 public:  4     //...constructor...
 5     virtual Item_Base* clone()const {return new Item_Base(*this)}  6 
 7     virtual double net_price(size_t) const;  8     virtual ~Item_Base(){}  9 private: 10     string isbn; 11 protected: 12     double price; 13 }; 14 
15 class Bulk_item : public Item_Base 16 { 17 public: 18     //...constructor...
19     virtual Bulk_item* clone()const {return new Bulk_item(*this)} 20 
21     double net_price(size_t) const; 22 private: 23  size_t min_qty; 24     double discount; 25 }; 26 
27 class Disc_item : public Item_Base 28 { 29 public: 30     //...constructor...
31     virtual Disc_item* clone()const {return new Disc_item(*this)} 32 
33     double net_price(size_t)const; 34 private: 35  size_t quantity; 36 };

clone虚関数をコピーします.派生クラスの戻りタイプがベースクラスインスタンスの戻りタイプと完全に一致する必要がある場合、例外があります.虚関数のベースクラスインスタンスがクラスタイプの参照またはポインタを返す場合、その虚関数の派生クラスインスタンスはベースクラスインスタンスが返すタイプの派生クラス(またはクラスタイプのポインタまたは参照)を返すことができます.clone関数の定義があれば、ハンドルクラスの構造関数について簡単です.
 1 class Sales_item  2 {  3 public:  4  Sales_item():h(){}  5     Sales_item(const Item_Base &item):h(item.clone()) {}  6     const Item_Base & operator*() const{return *h}  7     const Item_Base * operator->() const {return h.operator->}  8 private:  9     Handle<Item_Base> h; 10 };

このように処理する場合、顧客コードセグメントはnew操作を行う必要もなく、すべての割り当てと解放はSales_item内部完了.
1 vector<Sales_item> vItem; 2 vItem.push_back(Disc_Item()); 3 vItem.push_back(Bulk_item());