динамическое создание объектов в C ++ - PullRequest
2 голосов
/ 28 октября 2008

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

Существует еще одна связанная ситуация, когда вы используете схему передачи сообщений (обычно через TCP / IP), где каждое сообщение является объектом. Мне нравится реализовывать этот шаблон, позволяя каждому сообщению сериализовать себя в некоторый интерфейс потока сериализации, который хорошо работает и достаточно чист на отправляющей стороне, но на получателе я всегда проверяю заголовок сообщения, чтобы определить тип, затем создание соответствующего объекта сообщения с использованием шаблона из связанной статьи, а затем его десериализация из потока. Иногда я реализую это так, чтобы конструирование и десериализация происходили одновременно как часть конструктора, что выглядит более RAII, но это небольшое утешение для путаницы операторов if / else, выясняющих тип.

Есть ли лучшие решения там? Если вы собираетесь предложить стороннюю библиотеку, она должна быть бесплатной (и в идеале с открытым исходным кодом), и я был бы признателен, если бы вы могли объяснить, как библиотека выполняет этот подвиг.

Ответы [ 6 ]

4 голосов
/ 28 октября 2008

Я думаю, что вы спрашиваете, как сохранить код создания объекта вместе с самими объектами.

Это обычно то, что я делаю. Предполагается, что есть некоторый ключ, который дает вам тип (тег int, строка и т. Д.). Я создаю класс, который имеет карту ключей с фабричными функциями и функцию регистрации, которая берет ключ и фабричную функцию и добавляет ее к карте. Существует также функция create, которая берет ключ, просматривает его на карте, вызывает фабричную функцию и возвращает созданный объект. В качестве примера возьмем ключ int и поток, содержащий остальную информацию для построения объектов. Я не тестировал и даже не компилировал этот код, но он должен дать вам представление.

class Factory
{
    public:
    typedef Object*(*Func)(istream& is);
    static void register(int key, Func f) {m[key] = f;}
    Object* create(key, istream& is) {return m[key](is);}
    private:
    std::map<key, func> m;
}

Затем в каждом классе, производном от подобъекта, вызывается метод register () с соответствующим ключом и фабричным методом.

Чтобы создать объект, вам просто нужно что-то вроде этого:

while(cin)
{
    int key;
    is >> key;
    Object* obj = Factory::Create(key, is);
    // do something with objects
}
4 голосов
/ 28 октября 2008

То, что вы здесь описываете, называется шаблоном factory . Вариант - шаблон builder .

3 голосов
/ 28 октября 2008

Я бы предложил прочитать C ++ FAQ Lite вопросы, касающиеся сериализации и десериализации .

Там есть много деталей, которые я не могу суммировать в своем ответе, но эти часто задаваемые вопросы охватывают создание объектов, типы которых известны только во время выполнения.

В частности:

# 36,8

Хотя на самом базовом уровне вы могли бы реализовать фабрику, подобную:

Base* Base::get_object(std::string type)
{
    if (type == "derived1") return new Derived1;
    if (type == "derived2") return new Derived2;
}
0 голосов
/ 28 октября 2008

Основная операция фабричного шаблона - сопоставление идентификатора с (обычно новым) экземпляром определенного типа, где тип зависит от некоторого свойства идентификатора. Если вы этого не сделаете, то это не фабрика. Все остальное - дело вкуса (то есть производительность, ремонтопригодность, расширяемость и т. Д.).

0 голосов
/ 28 октября 2008

Если я что-то упускаю, вам не нужен static_cast для создания объекта, тип времени выполнения которого является подклассом типа, который вы должны возвращать с фабрики, а затем использовать его полиморфно:

class Sub1 : public Super { ... };
class Sub2 : public Super { ... };

Super *factory(int param) {
    if (param == 1) return new Sub1();
    if (param == 2) return new Sub2();
    return new Super();
}

int main(int argc, char **argv) {
    Super *parser = factory(argc);
    parser->parse(argv); // parse declared virtual in Super
    delete parser;
    return 0;
}

Паттерн, о котором говорит OP в вопросе, который вы упоминаете, заключается в том, чтобы получить Super * откуда-то, а затем привести его к типу времени выполнения, изучив RTTI и имея предложения if / else для всех подклассов, известных программисту. Это полная противоположность «Я использую объект полиморфно, как только он создан».

Теоретически, мой предпочтительный подход к десериализации - это цепочка ответственности: писать фабрики, способные просматривать сериализованные данные (включая заголовок типа) и самостоятельно решать, могут ли они построить объект из них. Для эффективности, пусть фабрики регистрируют, какие типы им интересны, чтобы они не все смотрели на каждый входящий объект, но я здесь это опускаю:

Super *deserialize(char *data, vector<Deserializer *> &factories>) {
    for (int i = 0; i < factories.size(); ++i) { // or a for_each loop
        Super *result = factories[i]->deserialize(data);
        if (result != NULL) return result;
    }
    throw stop_wasting_my_time_with_garbage_data();
}

На практике я часто заканчиваю тем, что в любом случае пишу большой ключ, например, только с перечисляемым типом, некоторыми именованными константами и, возможно, виртуальным методом десериализации, вызываемым после конструирования:

Super *deserialize(char *data) {
    uint32_t type = *((uint32_t *)data); // or use a stream
    switch(type) {
        case 0: return new Super(data+4);
        case 1: return new Sub1(data+4);
        case 2: return new Sub2(data+4);
        default: throw stop_wasting_my_time_with_garbage_data();
    }
}
0 голосов
/ 28 октября 2008

Читайте классику Gang Of Four aka GOF . Рассмотрим [этот сайт [(http://www.dofactory.com/Patterns/PatternAbstract.aspx) для Фабрики и другие шаблоны в C #.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...