2011-01-16 5 views
2

можно ли возвратить экземпляр объекта с использованием переданного типа name (string) в C++? У меня есть базовый абстрактный класс Base и несколько производных. Пример кода:Сделать объект по его названию

class Base 
{ 
    /* ... */ 
}; 

class Der1 : public Base 
{ 
    /* ... */ 
}; 

class Der2 : public Base 
{ 
    /* ... */ 
}; 

И мне нужна функция, как:

Base *objectByType(const std::string &name); 

Количество классов дериватов изменчивы, и я не хочу, чтобы сделать что-то подобное переключение name и возвращение руками новый тип объекта. Возможно ли в C++ делать это автоматически в любом случае?

p.s. использование должно выглядеть:

dynamic_cast<Der1>(objectByType("Der1")); 

мне нужна чистый C++ код (кроссплатформенный). Использование boost допустимо.

+0

Звучит специфический для ОС вопрос. О какой ОС мы говорим? –

+0

@sad_man, посмотрите на обновление, пожалуйста. Мне нужна кроссплатформенная версия. – Ockonal

+0

Как можно «изменить число производных»? Вы хотите назвать производные от внешней библиотеки, которая скомпилирована после вашего приложения? –

ответ

2

Есть хороший трюк, который позволяет вам написать factory method без последовательности if...else if....

(обратите внимание, что, AFAIK, это действительно невозможно сделать то, что вы хотите в C++, как этот код генерируется во время компиляции. А «фабричный метод» Design Pattern существует для этой цели)

Во-первых, вам определите global repository для ваших производных классов. Он может быть в форме std::map<std::string, Base*>, то есть отображает имя производного класса в an instance этого класса.

Для каждого производного класса вы определяете default constructor, который добавляет объект этого класса в репозиторий под именем класса.Вы также определить статический экземпляр класса:

// file: der1.h 
#include "repository.h" 

class Der1: public Base { 
public: 
    Der1() { repository[std::string("Der1")] = this; } 
}; 

// file: der1.cpp 
static Der1 der1Initializer; 

Конструкторов статических переменных выполняются еще до main(), так что, когда ваш main начинается у вас уже есть репозиторий инициализирован с экземплярами всех производных классов.

Ваш заводской метод (например, Base::getObject(const std::string&)) должен искать карту репозитория для имени класса. Затем он использует метод clone() объекта, который он находит, чтобы получить новый объект того же типа. Разумеется, вам необходимо реализовать clone для каждого подкласса.

Преимущество этого подхода заключается в том, что при добавлении нового производного класса ваши добавления ограничиваются только файлом (-ами), реализующим новый класс. Репозиторий и заводской код не изменятся. Конечно, вам все равно придется перекомпилировать вашу программу.

+2

Конструктор по умолчанию - это НЕОБХОДИМЫЙ способ сделать это. Просто создайте обычную глобальную переменную, такую ​​как 'int', которая инициализируется вызовом функции настройки фабрики. например 'static int g_factoryDer1Registration = Der1Factory :: Register();' Или создать экземпляр вложенного вспомогательного класса. Но не создавайте экземпляр типа, который завод производит, чтобы зарегистрировать фабрику, которая всячески нарушает принцип единой ответственности. –

+0

@Ben Voigt: спасибо, что указали это. Действительно, использование выделенного метода 'Der1Factory :: Register()' выглядит лучше. – davka

+0

Хорошая идея и без операторов switch/if. – Ockonal

0

Да, это возможно! Проверьте этот очень забавный класс под названием Activator Вы можете создать все на Type и string и даже дать список параметров, поэтому метод вызовет соответствующий конструктор с наилучшим набором аргументов.

+1

Я не верю, что задан вопрос .Net. –

+0

Является ли он кроссплатформенным и доступен для C++? Я вижу, что это основано на .Net или что-то вроде этого. – Ockonal

+3

Неправильный язык, неправильная структура (если только OP не упомянул, что он использует C++/CLI вместо C++, что маловероятно). –

0

Если я не понял, ключевое слово typeid должно быть частью того, что вы ищете.

+0

№ 'typeid' обеспечивает сопоставление типов с типами, описывающими объекты (включая имя типа в виде строки), но обратное сопоставление недоступно в C++. – Philipp

2

Это невозможно сделать на C++.

Один из вариантов - написать фабрику и включить имя, переданное, но я вижу, что вы не хотите этого делать. C++ не предоставляет никакой реальной поддержки отражения во время выполнения за пределами dynamic_cast, поэтому этот тип проблемы трудно решить.

+1

вы можете написать завод без 'switch' - см. Мой ответ ниже. – davka

0

Невозможно. Вы должны написать функцию objectByType себя:

Base* objectByType(const std::string& name) { 
    if (name == "Der1") 
    return new Der1; 
    else if (name == "Der2") 
    return new Der2; 
    // other possible tests 
    throw std::invalid_argument("Unknown type name " + name); 
} 
0

C++ не поддерживает reflection.

По-моему, это единственная точка, где Java бьет C++.
(ope не получить слишком много голосов для этого ...)

Вы можете достичь чего-то подобного, используя специальный препроцессор, аналогичный тому, как MOC делает для Qt.

Смежные вопросы