C++基本功和 Design Pattern系列 Base Class Design

來源:互聯網
上載者:User

在OO中Base Class最多的應用有2種方式,第一種是interface,也就是基類是個abstract class, 最典型的例子是Factory Design Pattern. 第二種方式是Base Class 提供一種機制,然後提供幾個Virtual Function,允許使用者對這個類進行重載,從而達到“配置” (customization)的目的。 其典型的代表是 MFC,通過繼承提供訊息傳遞機制的CWin類的OnPaint等函數,實現更多更強大的功能。

這次就主要講講這兩種類的設計。

============== Abstract Interface ==============

對於Interface Class來說,基類一定是個Abstract Class. 其含義是一類物體的抽象概念。比如下面一個例子:

class Gun {
public:
    void Shoot    (void) = 0;
    ...
};

Gun是所有人的抽象概念,具體的實現起來可以為Eagle, AK47, MD5 等等。所有的槍都有射擊的功能,但是他們具體射擊的方式不同,因此只要提供一個Shoot的Interface,內容由衍生類別來實現。

在具體設計 Interface Class的時候,有幾個需要注意的內容:

1. 最好只提供 pure virtual function。 這樣做能夠使Interface Class更加單一,更加純淨,更加漂亮,更容易維護。Interface就只能是Interface,不應該提供其他任何東西。因為Interface僅僅是一個描述。

2. 最好沒有data member。Data Member往往會限制 Interface Class的靈活性。而且有Data Member,一般就要有Accessor Function,使得Interface Class不再純潔。。。。。。

3. 如果不得不用Data Member,那麼一定要提供帶參數的Constructor,並且把確省的Constructor設成private.例如:

class InterfaceClass {
private:
    UINT32   _dataMember;
public:
    Base()      {};
    ...// other interface      
};

class Derived : public InterfaceClass {
public:
    Derived()    {};  
};

上面代碼中的Derived Constructor是合法的,但是由於寫代碼的人粗心大意,忘記給_dataMember附初值,很容易造成程式的崩潰。所以,對於有data member的Base Class 正確的寫法是:

class InterfaceClass {
private:
    UINT32   _dataMember;
public:
    Base( UINT32 data ) : _dataMember(data) {};
    ...//other interface
};

4. 在大部分情況下,Base Class的Destructor 一般要放到public 裡邊,並且要有實現,也就是說至少是空的實現,"{ }",即使是pure virtual destructor,這個 {}也是不能少的。因為還很多情況下,都需要通過Interface 調用Destructor來釋放object. 如:

class InterfaceClass {
public:
    ~InterfaceClass() {};  
};

======= Derived Class的管理 =======
對於Interface Class來說,如果Derived Class太多,是很難管理的,不過我們可以通過統一的ClassManager來實現。下面是個小例子:

// Interface
class InterfaceClass {
public:
    virtual void SomeInterface( void ) = 0;

private:
friend class ClassManager;  
    ~InterfaceClass()     = 0     {};  
};

// First Concrete Derived Class
class Derived1 : public InterfaceClass {
public:
    virtual void SomeInterface( void )    { ... };

private:
friend class ClassManager;  
    Derived1()   {...};
    ~Derived1()   {...};
};

// Second Concrete Derived Class
class Derived2 : public InterfaceClass {
public:
    virtual void SomeInterface( void )    { ... };

private:
friend class ClassManager;  
    Derived2()   {...};
    ~Derived2()   {...};
};

// Class Manager
// Singleton
class ClassManager
{
public:
    static ClassManager* GetInstance( void )
    {
       static ClassManager instance;
       return &instance;
    }
   
    InterfaceClass * GetDerived1(void) { return new Derived1() };
    InterfaceClass * GetDerived2(void) { return new Derived2() };  

private:
    ClassManager();  
    ~ClassManager();
};

============== Concrete Base Class ==============

Concrete Base Class是使用的比較多的。遊戲中經常有的object之間,差別非常的小,這些都可以通過Concrete Base Class來抽象,提高代碼重用的效率。下面是個小例子:

class StringArray {
public:
    void AddString() {...};
    void SearchString() {....};
    void SortString( void ) { StrategySortString(); };
    ...
private:
    virtual StrategySortString ( void )
    { ... };   // bubble sort
};  

class StringArray2 : public StringArray {
    ...
private:
    virtual StrategySortString ( void )
    { ... };   // quick sort
};  

假設我們有StringArray, 已經提供了所有基本的String Array操作,不過對於不同的Array輸入方式,刪除方式等,對應的排序方式的效率也不大相同。因此我們可以把sort設定成為 virtual,然後對於不同的StringArray的使用方式,再相對選擇正確的Derived Class就可以了。

對於Concrete Base Class 有幾點需要注意的:

1. Destructor。 相信這個不用多說了,一定要 virtual, 而且要保證資源釋放的乾淨。

2. 使用non-virtual interface. 這個是C++大師們的結論。其原因是因為Derived class在重載Virtual的時候,在很多情況下會影響到基類裡提供的機制的正常運作。如果使用non-public virtual,可以使得程式更加靈活,並且進程pre/post condition的檢查。比如:

class StringArray {
public:
    void SortArray ( )
    {
       // Pre-condition Check
       StrategySoryArray();
       // Post-condition Check
    };  
private:
    virtual void StrategySoryArray( void ) { ... };
};

這樣通過 pre/post condition check, 可以最大程度的保證基類的使用者不會犯錯誤。從而避免程式bug.

3. 盡量使用 private virtual. Protected virtual 只有在繼承類需要調用基類實現的時候,才應該放在protected裡。比較著名的是clone函數:

    class Base {
    protected:
       Base * clone ( void ) {};
    };

    class Derived : Base{
    protected:
       Base * clone ( void ) { Base::Clone(); ... };
    };

還有其他的一些方面,那就和基本的class設計大同小異了。不過還有一個重要的方面,就是Operator 和 Copy Constructor.

============== Operator ==============

在Base Class裡邊,所有的 operator中最重要的就是operator =了。對於 Abstract Class 來說,最好是禁止使用 Operator =。 為什麼呢?讓我們來看看代碼:

class Base {
public:
    virtual void Interface( void ) = 0;
    ...  
};

class Derived1 : public Base{
private:
    _x;
};

class Derived2 : public Base{
private:
    _x;
    _y;
};

Base *pD1 = new Derived1();
Base *pD2 = new Derived2();

*pD1 = *pD2;

也許 *pD1 = *pD2並不常見,但是確實是合法的代碼,可以通過編譯器的檢查。但是*pD1 = *pD2 調用的是 Base 的 operator = , 在Base 並沒有提供operator =的情況下,使用的是預設的位拷貝操作。這個預設的拷貝操作只拷貝Base Class的記憶體,而忽略了Derived Class的記憶體。也就是說 _x _y都沒有被拷貝。

如果我們在 Base Class 裡提供一個 virtual operator =, 在一定程度上能夠解決問題,但是 overload 的operator 必須進行類型轉換,例如:

Base & Derived1::Operator = (Base & x) {
    ....
    static_cast<Derived1 *>(x)
    ....
};

但是下面的代碼仍然可以編譯通過,產生錯誤的結果:

*pD1 = *pD2;

因此,對與 abstract class 來說,最方便的就是禁止 operator = , 把它設成 private.

對於Concrete Base Class來說,要麼提供 operator = ,要麼放在private 裡,但是要在Derived Class裡調用 Base Class 的 operator = 在例如:

class Base {
    ...
public:
    Base & operator = ( Base & );
};

class Derived : public Base{
    ...
public:
    Derived & operator = ( Derived & X )
    {
       Base::operator = ( X );
       ...
       // own assignment operations
    }
};

這種方法不支援cast up,也就是:
    Base * pBase1 = new Derived();
    Base * pBase2 = new Derived();
    *pBase1 = *pBase2;
   
稍微好點的方式是把 Base 的 operator = 放在 protected裡邊,但是也不是解決根本的問題。最好的解決方案是使用前面講到的virtual clone 和 construct實現。如:

class Base {
    virtual Base * construct (void) { return new Base(); };
    virtual Base * clone(void) { return new Base(*this); };
};

class Derived : public Base {
    virtual Base * construct (void)
    { return new Derived(); };
   
    virtual Base * clone(void)
    { return new Derived(*this); };
};

同時禁止Base的 opeartor =。但是這樣的效率會有所降低。總之,最好的方法是使用 abstract interface,同時禁止base使用 private operator =.

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.