動態2:由類名建立對象

來源:互聯網
上載者:User

前言:同上文一樣,本文源於對另一位朋友的問題的解答
(參見文章http://community.csdn.net/Expert/topic/3202/3202729.xml?temp=5.602664E-02)

C++不是動態語言,所以沒法從語言機制上實作類別的動態建立,但這樣的需求卻有可能存在,一個類似的例子便是MFC中CWnd類的Create方法,其第一個參數為Window Class的名字,這就允許使用者通過class的名字來建立相應的視窗。

要想實現這一點,必須有一個“管理中心”,用於登記類的名字,並且通過名字能夠調用某個方法來建立相應的類。結合類工廠的設計思想,這裡我們讓一套繼承體系中的基類作為“管理中心”,由它來維護所有衍生類別的必要資訊,包括類名和工廠函數,這二者必須建立起映射關係,map是不錯的選擇。定義了一個衍生類別後,它就自動向基類進行註冊,可是如何?自動呢?先看看程式吧:

// 為編寫方便,下面的代碼都位於.cpp檔案中
#ifdef _MSC_VER
#pragma warning(disable: 4786)
#endif
#include <iostream>
#include <map>
using namespace std;

class Base
{
public:
    virtual void Print() // 測試用
    {
        cout << "This is Base" << endl;
    }
protected:
    typedef Base* (*ClassGen)(); // 聲明函數指標
    static void Register(const char* class_name, ClassGen class_gen) // 註冊函數
    {
        class_set.insert(map<const char*, ClassGen>::value_type(class_name, class_gen));
    }
public:
    static Base* Create(const char* class_name) // 工廠函數
    {
        map<const char*, ClassGen>::iterator iter;
        if((iter = class_set.find(class_name)) != class_set.end())
        {
            return ((*iter).second)();
        }
        return NULL;
    } 
protected:
    static map<const char*, ClassGen> class_set; // 儲存子類資訊
};

map<const char*, Base::ClassGen> Base::class_set; // 靜態成員變數定義

class Derived : public Base
{
public:
    struct DerivedRegister // 輔助類,用於註冊 
    {
        DerivedRegister()
        { // 註冊子類,雖然map能保證唯一,但仍只註冊一次
            static bool bRegistered = false;
            if(!bRegistered)
            {
                Base::Register("Derived", Derived::Create); // 註冊子類資訊 
                bRegistered = true;
            }
        }    
    };
    static Base* Create() // 工廠函數
    {
        return new Derived;
    }
public:
    virtual void Print() // 測試用
    {
        cout << "This is Derived" << endl;
    }
};

static Derived::DerivedRegister Derived_for_registering; // 沒有其它機制能夠保證在全域空間調用註冊函數,
// 不得已,定義一個全域變數來完成這項光榮的任務,看起來有點齷齪

int main()
{
    Base* pDerived = Base::Create("Derived"); // 類名可以動態輸入
    if(pDerived) pDerived->Print(); // 建立成功調用虛函數
    else cout << "Create error" << endl;
    system("pause");
    return 0;
}

上面這樣實現,有的朋友可能覺得使用起來很麻煩,實際上我們用宏定義來改造一下就會非常漂亮,代碼呆會兒就會看到。還是說說自動註冊功能吧,首先,為什麼要實現自動註冊呢?這是為了最大程度上隱藏實現,實際上採用手動註冊也可以,只不過我們得在主函數裡一一調用每個類的註冊函數。那我們是如何?自動註冊的呢?方法是將註冊代碼放到一個輔助類的建構函式中,然後定義一個該類的靜態全域變數,這樣建構函式便被調用了,:),缺點是多了一個額外的對象,除了用於註冊外,它什麼用也沒有(最初我沒有採用輔助類,而是直接在衍生類別的建構函式中註冊,然後定義衍生類別的全域變數,這樣明顯會浪費空間,採用輔助類便將額外開銷降低到了最小)。

下面讓我們瞧瞧用宏改造後的模樣:

#ifdef _MSC_VER
#pragma warning(disable: 4786)
#endif
#include <iostream>
#include <map>
using namespace std;

// 用於聲明具有動態建立功能的基類 
#define DECLARE_DYNCRT_BASE(base) /
public: /
    typedef base* (*ClassGen)(); /
    static void Register(const char* class_name, ClassGen class_gen) /
    { /
        class_set.insert(map<const char*, ClassGen>::value_type(class_name, class_gen)); /
    } /
public: /
    static base* Create(const char* class_name) /
    { /
        map<const char*, ClassGen>::iterator iter; /
        if((iter = class_set.find(class_name)) != class_set.end()) /
        { /
            return ((*iter).second)(); /
        } /
        return NULL; /
    } /
protected: /
    static map<const char*, ClassGen> class_set; 

// 用於實現基類 
#define IMPLEMENT_DYNCRT_BASE(base) /
map<const char*, base::ClassGen> base::class_set;

// 用於聲明一個能夠被動態建立的類 
#define DECLARE_DYNCRT_CLASS(derived, base) /
public: /
    struct derived##Register /
    { /
        derived##Register() /
        { /
            static bool bRegistered = false; /
            if(!bRegistered) /
            { /
                base::Register(#derived, Create); /
                bRegistered = true; /
            } /
        } /
    }; /
    static base* Create() /
    { /
        return new derived; /
    }

// 用於實現一個能夠被動態建立的類  
#define IMPLEMENT_DYNCRT_CLASS(derived) /
static derived::derived##Register derived##_for_registering; 

// 測試
class Base
{
    DECLARE_DYNCRT_BASE(Base) // 聲明動態基類 
    DECLARE_DYNCRT_CLASS(Base, Base) // 基類自己也可以動態建立 
public:
    virtual void Print() 
    {
        cout << "This is Base" << endl;
    }
};

IMPLEMENT_DYNCRT_BASE(Base) // 實現動態基類 
IMPLEMENT_DYNCRT_CLASS(Base) // 實現動態類

class Derived : public Base
{
    DECLARE_DYNCRT_CLASS(Derived, Base) // 聲明動態類 
public:
    virtual void Print() 
    {
        cout << "This is Derived" << endl;
    }
};

IMPLEMENT_DYNCRT_CLASS(Derived) // 實現動態類

int main()
{
    Base* pBase = Base::Create("Base"); // 類名可以動態輸入
    if(pBase) pBase->Print(); // 建立成功調用虛函數
    else cout << "Create Base error" << endl;
    
    Base* pDerived = Base::Create("Derived"); // 類名可以動態輸入
    if(pDerived) pDerived->Print(); // 建立成功調用虛函數
    else cout << "Create Derived error" << endl;
    
    system("pause");
    return 0;
}

上面的宏定義可以重複利用(是不是有點似曾相識,是的,MFC中在對序列化和動態建立等支援時用到了類似的宏),在使用的時候,只需簡單的使用這四個宏,即可實現動態建立,感覺還不錯吧,不過提醒一點,其中的兩個IMPLEMENT宏都應該放在.cpp檔案中。

這種方法還是存在一些缺點的,不過本文主要是提供一種思路,可以根據具體的情況再作相應變動。

(freefalcon於2004.09.19)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.