2012-03-30 63 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; 
}