一、容器與繼承
在容器中保存有繼承關系的對象時,如果定義成保存基類對象,則派生類將被切割,如果定義成保存派生類對象,則保存基類對象又成問題(基類對象將被強制轉換成派生類對象,而派生類中定義的成員未被初始化)。
唯一的可行的選擇是容器中保存對象的指針。但是需要用戶管理對象和指針。C++中一個通用的技術是包裝類(cover)或句柄類(handle)。用句柄類存儲和管理類指針。
句柄類大體上完成兩方面的工作:
-
管理指針,這與智能指針的功能類似。
-
實現多態,利用動態綁定,是得指針既可以指向基類,也可以指向派生類。
包裝了繼承層次的句柄有兩個重要的設計考慮因素:
-
像對任何保存指針的類一樣,必須確定對復制控件做些什么。包裝了繼承層次的句柄通常表現得像一個智能指針或者像一個值。
-
名柄類決定句柄接口屏蔽還是不屏蔽繼承層次,如果不屏蔽繼承層次,用戶必須了解和使用基本層次中的對象(objects in theunderlying hierarchy)。
下面通過一個我自己寫的一個簡單的例子來說明這個問題:
這個例子程序包括一個基類,一個派生類,還有一個句柄類。
其中,基類有2個私有成員,數值m_base和程序名字name。派生類有一個新的私有成員,m_der。
派生類和基類有虛函數compute。基類的compute它計算基類成員m_base平方。派生類的compute計算m_base平方和m_der之和。
句柄類有兩個數據成員,分別是指向引用計數的指針( 這里必須是指針,復制時引用計數復制指針的值,保證一個實例化對象只有一個引用計數)和指向基類或者是其派生類的指針。
#include<iostream> #include<string> #include<exception> using namespace std; // base class class Base { public: //basic constructor Base(int m_base = 1, string name = "Base") : m_base(m_base), name(name) { cout << "Base constructor called!" << endl; } //copy constructor Base(const Base &base) : Base(base.m_base, base.name) { cout << "Base copy called" << endl; } virtual Base *clone() const { return new Base(*this); } const string getName() { return name; } virtual int compute() const { return m_base * m_base; } virtual ~Base(){ cout<<"Base deleted"<<endl; } protected: int m_base; string name; }; class Derived : public Base { public: //basic constructor Derived(int m_base, string name, int m_der) : Base(m_base, name), m_der(m_der) { cout << "Derived constructor called" << endl; } //copy constructor Derived(const Derived &derived) : Derived(derived.m_base, derived.name, derived.m_der) { cout << "Derived copy called" << endl; } virtual Derived *clone() const { return new Derived(*this); } virtual int compute() const { //調用父類中定義的方法 return Base::compute() + m_der; } virtual ~Derived(){ cout<<"Derived deleted"<<endl; } private: int m_der; }; class Handler { public: //默認構造函數 Handler() : pBase(NULL), use(new int(1)) { } //一般構造函數 Handler(const Base &item) : pBase(item.clone()), use(new int(1)) { } //復制構造函數 //每復制一次,引用計數就加1 Handler(const Handler &ref) : pBase(ref.pBase), use(ref.use) { ++*use; } //重載賦值操作符 Handler &operator=(const Handler &right) { ++*(right.use); decrese_use(); pBase = right.pBase; use = right.use; return *this; } //重載箭頭操作符 const Base *operator->() const { if (pBase) return pBase; else throw logic_error("unbound Handler!"); } //重載解引用操作符 const Base &operator* () const{ if(pBase) return *pBase; else throw logic_error("unbound Handler"); } void print_use() { cout << pBase->getName() << " use: " << *use << endl; } //析構函數 ~Handler() { decrese_use(); } private: //此處必須使用指針,保證一個Base實例只對應一個引用計數 int *use; Base *pBase; void decrese_use() { if (--*use == 0) { cout << pBase->getName() << " is going to be deleted!" << endl; delete pBase; } } }; int main() { Handler h1(Base(2,"Base")); h1.print_use(); cout<<"Base compute:"<<(*h1).compute()<<endl; Handler h2(h1); h2.print_use(); cout<<"Base compute:"<<(*h2).compute()<<endl; cout<<"-------------------------------------"<<endl; Handler h3(Derived(3,"derived",3)); h1=h3; h1.print_use(); cout<<"Derived compute:"<<(*h1).compute()<<endl; cout<<"system automatic delete begin"<<endl; return 0; }
二、句柄類
句柄類Handle 有3個構造函數:默認構造函數,復制構造函數,和接收基類Base對象的構造函數。為了保證 在接收基類Base對象的構造函數中 復制具體對象的時候實現動態調用,得到正確類別的實例,我們在類中定義了虛函數clone。
Base
virtual Base *clone() const { return new Base(*this); }
Derived
virtual Derived *clone() const { return new Derived(*this); }
三、運行結果
主函數調用:
int main() { Handler h1(Base(2,"Base")); h1.print_use(); cout<<"Base compute:"<<(*h1).compute()<<endl; Handler h2(h1); h2.print_use(); cout<<"Base compute:"<<(*h2).compute()<<endl; cout<<"-------------------------------------"<<endl; Handler h3(Derived(3,"derived",3)); h1=h3; h1.print_use(); cout<<"Derived compute:"<<(*h1).compute()<<endl; cout<<"system automatic delete begin"<<endl; return 0; }
輸出:
Base constructor called! Base constructor called! Base copy called Base deleted Base use: 1 Base compute:4 Base use: 2 Base use: 2 Base compute:4 ------------------------------------- Base constructor called! Derived constructor called Base constructor called! Derived constructor called Derived copy called Derived deleted Base deleted derived use: 2 derived use: 2 Derived compute:12 system automatic delete begin Base is going to be deleted! Base deleted derived is going to be deleted! Derived deleted Base deleted
主函數中使用Base對象創建了Handler對象h1,並由h1構造Handler對象h2,通過輸出可以發現Handler對象的引用計數由1變為2。然后使用Derived對象創建Handler對象h3,並將其賦值給h1,對h1,h3 輸出其引用計數,可知引用計數均為2.