Заводская функция, возвращающая объект определенного типа — как это сделать лучше

У меня есть 3 класса B, C, D, которые происходят от одного базового класса A:

class A
{
// body
};

class B : public A
{
// body
};

class C : public A
{
// body
};

class D : public A
{
// body
};

Я хочу создать фабричную функцию, которая позволит мне создать объект определенного типа (B, C или D) и вернуть его как указатель на класс A:

typedef std::shared_ptr<A> a_ptr;

a_ptr createObject(int type)
{
    switch(type)
    {
    case 0:
        return a_ptr(new B());
    break;
    case 1:
        return a_ptr(new C());
    break;
    case 2:
        return a_ptr(new D());
    break;
    }
}

А затем, если у меня есть, например, указатель типа B, я бы хотел назначить ему объект B, созданный фабрикой. Единственное разумное решение, которое пришло мне в голову, было бы:

std::shared_ptr<B> b = std::shared_ptr<B>(dynamic_cast<B*>(createObject(0)));

Но выглядит некрасиво. Есть ли лучшее решение для этого? Или, может быть, мне стоит попробовать другой способ создания объектов с моей функцией?

Если вы создаете объекты на основе значения времени выполнения (здесь int type), тогда нет возможности правильного ввода возвращаемого значения (это было бы возможно, если бы createObject был шаблоном, но тогда вы могли бы создавать только статически), так что гипс необходим. Вы могли бы придумать способы скрыть это, но ИМХО, это плохая идея. Отливки должны быть заметными.   —  person Piotr Chojnacki    schedule 07.03.2013

C ++ статически типизирован. То есть вы должны знать тип каждого выражения во время компиляции. (В вашем случае это должен быть тип A.)   —  person Piotr Chojnacki    schedule 07.03.2013

Обратите внимание, что shared_ptrs имеют свои собственные методы преобразования.   —  person Piotr Chojnacki    schedule 07.03.2013

См. также:  rake db: миграция прерывается из-за разницы в версии rake
Понравилась статья? Поделиться с друзьями:
IT Шеф
Комментарии: 2
  1. Piotr Chojnacki

    Если вы хотите выполнить понижающее преобразование shared_ptr во время выполнения, лучше используйте std::dynamic_pointer_cast<>:

    std::shared_ptr<B> b = std::dynamic_pointer_cast<B>(createObject(0));
    

    Если тип создаваемого объекта определяется во время выполнения, и вы делаете попытку проверить, имеет ли возвращаемый объект тип B, то я не вижу лучшей альтернативы, чем динамическое приведение вниз.

    С другой стороны, если вы знаете со 100% уверенностью, что заостренный объект имеет тип B, вы можете использовать std::static_pointer_cast<>:

    std::shared_ptr<B> b = std::static_pointer_cast<B>(createObject(0));
    

    Однако в тот момент, когда вы знаете это со 100% уверенностью, я не понимаю, почему такая инструкция не должна выглядеть:

    std::shared_ptr<B> b = std::make_shared<B>();
    

    Кроме того, я думаю, что важно отметить, что потребность в динамическом приведении в большинстве случаев является запахом кода. У вас есть общий базовый класс, поэтому вы можете избежать приведения типов. Виртуальные функции должны позволить вам получить различное поведение каждого класса. И если вам нужно выполнить приведение для доступа к чему-либо в производном классе, тогда ваш вызывающий код в любом случае будет большим случаем if-else или switch, что предполагает, что ваши классы, возможно, не должны быть связаны путем получения из общей базы. person Piotr Chojnacki; 07.03.2013

  2. Piotr Chojnacki

    Для небольшого количества «типов» ваш createObject выглядит неплохо. Это становится уродливым, только если есть много «типов». Тогда вам может быть лучше, если вы создадите тип сопоставления таблицы с фабричной функцией соответствующего класса. Например:

    std::map<int, std::function <a_ptr ()> > dispatcherTable = 
    {
        {0, createB },
        {1, createC },
        {2, createD }
    };
    

    Затем вы можете просто использовать свой «тип» в качестве индекса в таблице:

    new_a = dispatcherTable[a]();
    

    Эти createB, createC и т. Д. Могут быть статическими членами вашего фабричного класса или статическими членами классов B, C и т. Д. Как хотите.

    Это работает, но корпус переключателя более читабельный. И я думаю, что OP не об этом спрашивал. person Piotr Chojnacki; 07.03.2013

Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: