Полиморфные Перечисления - PullRequest
2 голосов
/ 30 марта 2012

Полиморфные Перечисления?

В C ++ мы часто используем полиморфизм, чтобы позволить старому коду обрабатывать новый код - например, если мы создадим подкласс интерфейса, ожидаемый функцией, мы можем передатьв новом классе и ожидаем, что он будет правильно работать с кодом, который был написан до того, как новый класс когда-либо существовал.К сожалению, с перечислениями, вы не можете сделать это, хотя бывают моменты, когда вы хотели бы.(Например, если вы управляли настройками для вашей программы и сохранили все их как значения перечисления, то было бы неплохо иметь перечисление settings_t, от которого наследуются все остальные перечисления, чтобы вы могли хранить все новыеперечислите в списке настроек. Обратите внимание, что, поскольку список содержит значения различных типов, вы не можете использовать шаблоны .)

Если вам нужно такое поведение, вы вынужденысохранить перечисления в виде целых чисел, а затем извлечь их с помощью типов типов, чтобы присвоить конкретное значение интересующей вас настройке.И вы даже не получите преимущество dynamic_cast, которое поможет вам обеспечить безопасность приведения - вам придется полагаться на тот факт, что в списке нельзя сохранить неправильные значения.

Я цитирую учебник по C ++ .

Кто-нибудь может объяснить, пожалуйста, более подробно и с некоторыми примерами, как работают полиморфные перечисления?А в случае, если у меня есть шаблоны?

1 Ответ

3 голосов
/ 30 марта 2012

Проще говоря, enum - это просто именованное постоянное значение, например:

enum Settings
{
   setting_number_0,
   setting_number_1,
   setting_number_2,      
};

. В приведенном выше примере setting_number_X - это просто именованная константа для значения X, так какЗначения перечисления начинаются с 0 и монотонно увеличиваются.

Сохранение их затем в контейнере определенного типа дает базовый тип хранения целых чисел, но все же может быть несколько безопасным для типов.Предположим, вы хотите добавить дополнительные конкретные типы настроек и сохранить их все в контейнере.

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;
}
...