Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Динамическое создание объекта


Автор: dAnIK SeNT 4.6.2003, 23:23
Дано:
класс base_class.
производные от него классы
child_xxxxxx
где xxxxx - чего-нибудь. например
child_first
child_second
child_third

функция типа
base_class *create_object(char *obj_type) {
}

obj_type - это как раз содержимое ххх, т.е. напрмер вызываем:

base_class *bc = create_object("first");

такой вызов должен создать объект типа child_first и вернуть указатель на него.

это вообще реально?

Автор: acp 4.6.2003, 23:33

Как следует из понимания ООП, то родителю можно присваивать потомка. Но в вышенаписанном тобой случае, по всей видимости, тебе функция всё время будет возвращать родителя, а никак не потомка.
Тут возможны два решения
1. или перегрузка функций
2. или void* (вроде так)

Автор: RAN 5.6.2003, 08:39
acp, а где ты функцию увидел, человек только её определение привёл. Не известно что это функция делает.

dAnIK SeNT, поищи какое-нибудь другое решение. Так как-то странно получается. Но это возможно за одним "но". Нельзя получив char* добавить вначале "child_" и создать такой класс, можно лишь написать код, в котором сам будешь по char* определять какой класс создать:

base_class *create_object(char* obj_type)
{
if(strcmp(obj_type, "first")) return new child_first;
return NULL;
}

Однако лучше это делать через константы. Определи в .h define'ы для каждого класса и тогда используй switch

#define CHILD_FIRST 1
#define CHILD_SECOND 2

base_class *create_object(int obj_type)
{

switch(obj_type) {
case CHILD_FIRST:
return new child_first;
case CHILD_SECOND:
return new child_second;
default:
return NULL;
}

}

И ещё раз ты возвращаешь base_class*, то будешь "видеть" в child_first только виртуальные функции.

Автор: acp 5.6.2003, 08:44
Цитата
acp, а где ты функцию увидел, человек только её определение привёл. Не известно что это функция делает.



Зато видно, что эта функция возвращает. Разве этого не достаточно?

Автор: vickr 5.6.2003, 08:59
С acp не совсем согласен. Указателю на родителя можно присваивать указатель на потомка (в свое время при мзучении модели СОМ и множественного наследования интерфейсов для меня это тоже было откровением smile.gif), но нужно делать преобразование типа.
RAN Привел вполне подходящее решение. Разумеется, для фиксированного набора дочерних классов. Строки или константы - это уже дело вкуса, в принципе, константы разумнее, согласен.
В качестве альтернативы можно предложить использовать шаблон ф-и, типа
Код

template<class TChild>
base_class *create_object()
{
       return static_cast<base_class*>(new TChild);
}

Сразу оговорюсь - это первое, что пришло в голову и поэтому привожу код как информацию к размышлению. Писалось прямо здесь.
Экспериментируйте, уважаемые smile.gif

Автор: Unregistered 5.6.2003, 13:09
Нужно использовать патерн "Фабрика классов"
Будет это выглядеть примерно так. Под рукой хелпов и компиляторов нет, поэтому наверняка будут ошибки(особенно в по отношению stl)

Код

//нижеследующий код менять при появлении нового класса не требуется
class TBaseClass;
typedef  TBaseClass* (*TCreateFunction)(void);
typedef   map<string, TCreateFunction>  TFunctionMap;
class TBaseClass
{
public:
  TBaseClass(){}
  static string ClassName( ) {return string("BaseClass");}
  static  TBaseClass* Create(void) { return new TBaseClass; }
};

class FabricClass
{
private:
 TFuctionMap m_Map;
public:
 static void Register(string str, TFunctionMap fun)
 {
       m_Map.insert(str, fun);
 }
 static TBaseClass* Create(string str)
 {
    TFunctionMap::iterator i = m_Map.find(str);
     if (i)
     {
        TFunctionMap func = i.second;
        return (*func)();
     }
     return NULL;  
 }
}  
//здесь кончается неизменяемый код

// далее идет только то, что касается  конкретно классов

class TBaseClass1 : public TBaseClass
{
public:
  TBaseClass1():TBaseClass(){}
  static string ClassName( ) {return string("BaseClass1");}
  static  TBaseClass* Create(void) { return new TBaseClass1; }
};

class TBaseClass2 : public TBaseClass
{
public:
  TBaseClass2():TBaseClass(){}
  static string ClassName( ) {return string("BaseClass2");}
  static  TBaseClass* Create(void) { return new TBaseClass2; }
};

//теперь используем
//сначала регистрация классов в фабрике

TFabricClass::Register(TBaseClass::ClassName(); &(TBaseClass::Create));
TFabricClass::Register(TBaseClass1::ClassName(); &(TBaseClass1::Create));
TFabricClass::Register(TBaseClass2::ClassName(); &(TBaseClass2::Create));

TBaseClass* base = TFabricClass::Create(string("BaseClass"));
TBaseCalss* base1 = TFabricClass::Create(string("BaseClass1"));
TBaseCalss* base2 = TFabricClass::Create(string("BaseClass2"));


Думаю, смысл понятен.

Автор: Vyacheslav 5.6.2003, 13:12
Предыдуший пост о фабрике классов - мой

Автор: dAnIK SeNT 5.6.2003, 13:36
2All
спасибо за ответы smile.gif. теперь по порядку.

Цитата
base_class *create_object(char* obj_type)
{
if(strcmp(obj_type, "first")) return new child_first;
return NULL;
}

это я понимаю. хотелось бы автоматизировать процесс.

Цитата
Однако лучше это делать через константы. Определи в .h define'ы для каждого класса и тогда используй switch
...

я бы так и сделал smile.gif. но этот пример почти не отличается от предыдущего smile.gif.

Цитата
И ещё раз ты возвращаешь base_class*, то будешь "видеть" в child_first только виртуальные функции.

конечно. интерфейс у всех этих классов одинаковый - описанный в абстрактном базовом классе.

2Vyacheslav
сходу в код не въехал (только начинаю работать с С++ smile.gif). но, похоже, это как раз то что мне надо. спасибо smile.gif

Автор: DENNN 5.6.2003, 14:16
Можно предложить альтернативу шаблонному описанию класса - макрос
Код

#define FABRIC(cl_type) \
new cl_type;\

void main (void)
{
base_class* pclass[2];
//создаем first
pclass[1]=FABRIC (first)
//создаем second
pclass[1]=FABRIC (second)

}

Автор: RAN 6.6.2003, 00:33
Vyacheslav, а от куда от термин "фабрика классов", я хотел бы почитать об этом, чтоб понять где это используют. Вообще мне кажется, что это лишнее, но это моё мнение. Ведь если приглядеться, то это тоже самое сравнение строк, только после сравнения не сразу создаётся класс, а вызывается static функция, которая создаёт класс. А вот идея DENNNа мне понравилась, но скорее всего имя класса dAnIK SeNT хотел создавать динамически.

Acp, ты сам написал, что родителю можно присваисать потомка. Между прочим это активно использовалось в OWL, но это было давно...

Автор: Vyacheslav 6.6.2003, 09:30
RAN
Основное примущество предложенного мною метода в том, что коды TBaseClass и TFabricClass не требуют модификации при добавлении новых классов, т.е. мы в принципе можем это вынести в ядро нашей системы (например dll) и забыть. Что же касается фабрики классов, действительно простешая фабрика классов предстваляет собой функцию с набором if, типа
Код

TBaseClass* CreateClass(AnsiString str)
{
  if (str == "BaseClass") return new TBaseClass;
  if (str == "BaseClass1") return new TBaseClass1;
  if (str == "BaseClass2") return new TBaseClass2;
  return NULL;
}

Но у нее есть недостаток - при появленнии нового класса эту функцию надо править. Что же касается патеррнов программирования, советую обратить внимание на книгу http://oz.by/books/more.phtml?id=101783

Автор: DENNN 8.6.2003, 15:36
Термин "фабрика классов" активно используется в COM-технологии. Там это действительно "фабрика классов", так как клиент абсолютно не участвует в механизме создания запрашиваемых объектов, а лишь использует их и управляет временем жизни. Такая технология очень удобна при создании "распределенных приложений". В твоем примере такой метод построения может обеспечить некоторый "запас прочности кода" при дальнейшем усовершенствовании.

Автор: Vyacheslav 9.6.2003, 09:30
"Фабрика классов" - это не порождение COM, а лишь одна из многочисленных реализаций известного паттерна. К тому же на мой вгляд, незачем применять технологию COM, если в этом нет особой необходимости.

Автор: RAN 10.6.2003, 07:46
Цитата
К тому же на мой вгляд, незачем применять технологию COM, если в этом нет особой необходимости.

Может ты и прав, но кто нас спрашивает, дядиньки из MicroSoft сказали надо, значит надо. Это теперь стандарт.

Автор: Fantasist 10.6.2003, 16:51
Цитата
Может ты и прав, но кто нас спрашивает, дядиньки из MicroSoft сказали надо, значит надо. Это теперь стандарт


А что, дядиньки из MicroSoft сказали, что надо применять COM даже там, где это не нужно? smile.gif

Автор: zabivator 14.9.2006, 12:04
А еще лучше покурить Александреску и его реализации фабрик и абстрактных фабрик
пример использования ( из реального кода ):
Код

#include <loki/Factory.h>
#include "Component.h"
//...
class IPort; // Component.h
class IInterface; // Component.h

class IDevice : public Loki::Factory<IPort,std::string,IPort* (*) ()>, public Loki::Factory<IInterface,std::string,IIntreface* (*) ()>
{
public:
//...
   IPort * Create<Port>( const std::string& id ); // Этого в коде нет, но это сгенерится засчет наследования от Loki::Factory<IPort*...
   IInterface * Create<Interface>( const std::string& id ); // Аналогично
//...
}

typedef Loki::AbstractFactory<(LOKI_TYPELIST_2(IPort,IInterface)> ComponentFactory;

class CSyncComponent : public Loki::ConcreteFactory<ComponentFactory, LOKI_TYPELIST_2(CSyncPort,CIPInterface)> 
{
   IPort* Create<Port>( const std::string& id ) // Аналогично
}


В итоге получаем абстракную фабрику IDevice, автоматически генерируемую при помощи LokI::AbstarcFactory Александреску, в которой еще живут две фабрики - фабрика объектов семейства IPort ( X25, CE1 и т.д. ), а также фабрику интерфейсов ( HDLC, SLIP. и т.д. ).
Вот так вот =) А представьте писать это все руками?

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)