2012-03-30 42 views
2

多態性Enums?多態性Enums

在C++中,我們經常使用多態,讓舊的代碼來處理新的 碼 - 例如,只要我們繼承由 函數預期的接口,我們可以在新的類通過,並期望它使用在新類存在之前編寫的代碼正確工作 。 不幸的是,使用枚舉,你不能真正做到這一點,即使 偶爾是你想要的。 (例如,如果你是 管理你的程序的設置,並將它們全部存儲爲 枚舉值,那麼可能會很好地從 獲得一個枚舉settings_t,其中所有其他枚舉都繼承了,以便可以存儲在設置列表中的每個 新枚舉。請注意,由於列表中包含不同類型的 值,不能使用模板

如果你需要這樣的行爲,你不得不店枚舉爲 整數,然後使用類型轉換來檢索它們以將 特定值分配給感興趣的設置。而且,您甚至不會獲得dynamic_cast的好處,以幫助您確保演員陣容是 安全 - 您必須依賴存儲在列表中的錯誤值不能爲 的事實。

我從C++ programming tutorial引用。

任何人都可以請更深入地解釋一些例子Polymorphic Enums是如何工作的嗎? 而在我有模板的情況下?

+2

我認爲這是從引用的文字多晶型'enums'別清晰至少從最簡單的意義上說,這是一種工作。一個'enum'基本上是一個具有基礎積分存儲類型的命名常量。在任何你想使用polymorhpic'enum'的地方,使用模板可能會有更優雅的解決方案。 – Chad 2012-03-30 14:46:52

+5

多態枚舉沒有多大意義,如果你認爲你需要它們,那麼你的設計很可能是以次充好的。如果你真的需要它們,那麼Boost.Variant應該可以工作。 – Pubby 2012-03-30 14:49:52

+0

@Chad你能幫助我更好地理解一些代碼片段的例子嗎? – Matteo 2012-03-30 14:55:29

回答

3

簡單地說,一個enum僅僅是一個命名爲恆定值,例如:

enum Settings 
{ 
    setting_number_0, 
    setting_number_1, 
    setting_number_2,  
}; 

在上述例子中,setting_number_X是簡單地爲值X命名常量,如枚舉值從0開始並增加單調。

保留這些,然後在某些類型的容器中給出了一個基本的整數存儲類型,但仍然可能有點類型安全。

std::vector<Setting> app_settings; 

// this works 
app_settings.push_back(setting_number_0); 

// this is a compile time failure, even though the underlying storage 
// type for Setting is an integral value. This keeps you from adding 
// invalid settings types to your container (like 13 here) 
app_settings.push_back(13); 

// but you also cannot (directly) add valid setting values (like 1) 
// as an integral, this is also a compile time failure. 
app_settings.push_back(1); 

現在,假設您想添加其他特定設置類型並將它們全部保存在容器中。現在

enum DisplaySettings 
{ 
    // ... 
}; 

enum EngineSettings 
{ 
    // ... 
}; 

,如果你想保留的所有設置在一個容器中,你不能安全。您可以將所有積分值存儲在容器std::vector<int>或類似的容器中,但這樣做會導致無法確定哪些整型屬於設置枚舉的內容。此外,由於類型不同,因此不能將它們存儲在單個類型安全的容器中。

正確的方法去了解這是將存儲設置的功能在容器中,這樣的事情:

#include <vector> 
#include <iostream> 

// This is our "base class" type so we can store lots of 
// different setting types in our container 
class setting_action 
{ 
public: 
    // we enable the setting by calling our function 
    void enable_setting() 
    { 
     setting_function_(this); 
    } 

protected: 
    // This is a function pointer, and we're using it to get some 
    // compile time polymorphism 
    typedef void (*setting_function_type)(setting_action* setting); 

    // these can only be constructed by derived types, and the derived 
    // type will provide the polymorhpic behavior by means of the 
    // above function pointer and based on the derived type's handler 
    setting_action(setting_function_type func) 
     : setting_function_(func) 
    { 
    } 

public: 
    ~setting_action() 
    { 
    } 

private: 
    setting_function_type setting_function_; 
}; 

// This is the derived type, and where most of the magic 
// happens. This is templated on our actual setting type 
// that we define below  
template <class Setting> 
class templated_setting_action 
    : public setting_action 
{ 
public: 
    templated_setting_action(Setting setting) 
     : setting_action(&templated_setting_action::enable_setting) 
     , setting_(setting) 
    { 
    } 

    // This function catches the "enable_setting" call from 
    // our base class, and directs it to the handler functor 
    // object that we've defined 
    static void enable_setting(setting_action* base) 
    { 
     templated_setting_action<Setting>* local_this = 
     static_cast<templated_setting_action<Setting>*>(base); 

     local_this->setting_(); 
    } 

private: 
    Setting setting_; 
}; 

// this is just a shorthand way of creating the specialized types 
template <class T> 
setting_action* create_specialized_setting_action(T type) 
{ 
    return 
     new templated_setting_action<T>(type); 
} 

// Our actual settings: 
// this one displays the user name  
struct display_user_name 
{ 
    void operator()() 
    { 
     std::cout << "Chad.\n"; 
    } 
}; 

// this one displays a short welcome message  
struct display_welcome_message 
{ 
    void operator()() 
    { 
     std::cout << "Ahh, the magic of templates. Welcome!\n"; 
    } 
}; 

// now, we can have one container for ALL our application settings 

std::vector<setting_action*> app_settings; 

int main() 
{ 
    // now we can add our settings to the container... 
    app_settings.push_back(create_specialized_setting_action(display_user_name())); 
    app_settings.push_back(create_specialized_setting_action(display_welcome_message())); 

    // and individually enable them 
    app_settings[0]->enable_setting(); 
    app_settings[1]->enable_setting(); 

    // also, need to delete each setting to avoid leaking the memory 
    // left as an exercise for the reader :) 
    return 0; 
}