Фабрика c ++ и проблема кастинга - PullRequest
2 голосов
/ 28 сентября 2010

У меня есть проект, в котором у меня есть много связанных классов Info, и я подумывал о создании иерархии, имея класс AbstractInfo, а затем набор производных классов, переопределяя реализации AbstractInfo по мере необходимости. Однако оказывается, что в C ++ использование класса AbstractInfo для создания одного из производных объектов не так просто. (см. этот вопрос, комментарий к последнему ответу)

Я собирался создать как фабричный класс, который создает объект Info и всегда возвращает объект AbstractInfo. Я знаю, что в C # это можно сделать с помощью интерфейсов, но в C ++ все выглядит немного иначе.

Даун-кастинг становится сложным делом и кажется подверженным ошибкам.

У кого-нибудь есть лучшее предложение для моей проблемы?

Ответы [ 6 ]

5 голосов
/ 28 сентября 2010

Вам не нужно унывать.Смотрите этот пример:

class AbstractInfo
{
public:
    virtual ~AbstractInfo() {}
    virtual void f() = 0;
};

class ConcreteInfo1 : public AbstractInfo
{
public:
    void f()
    {
        cout<<"Info1::f()\n";
    }
};

class ConcreteInfo2 : public AbstractInfo
{
public:
    void f()
    {
        cout<<"Info2::f()\n";
    }
};

AbstractInfo* createInfo(int id)
{
    AbstractInfo* pInfo = NULL;
    switch(id)
    {
    case 1:
        pInfo = new ConcreteInfo1;
        break;

    case 2:
    default:
        pInfo = new ConcreteInfo2;
    }

    return pInfo;
}


int main()
{

    AbstractInfo* pInfo = createInfo(1);
    pInfo->f();
    return 0;
}
2 голосов
/ 28 сентября 2010

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

1 голос
/ 28 сентября 2010
class AbstractInfo
{
  public:
    virtual ~AbstractInfo();
    virtual X f();
   ...
};

class Info_1 : public AbstractInfo
{
    ...
};

class Info_2 : public AbstractInfo
{
    ...
};

AbstractInfo* factory(inputs...)
{
    if (conditions where you would want an Info_1)
        return new Info_1(...);
    else if (condtions for an Info_2)
        return new Info_2(...);
    else
        moan_loudly();
}

Если вы не хотите, чтобы фабричный метод стал единым пунктом обслуживания, поскольку нижестоящий клиентский код добавляет типы Info, вместо этого вы можете предоставить некоторый механизм для клиентского кода, чтобы зарегистрировать методы для создания этих производных объектов. Обратитесь к книге «Шаблоны дизайна» от Gang of Four или найдите их в Google.

1 голос
/ 28 сентября 2010

Хотя обычно вы не можете перегружать типы возвращаемых данных в C ++, есть исключение для ковариантных типов возвращаемых данных

Пример взят из википедии:

 // Classes used as return types:
 class A {
 }

 class B : public A {
 }

 // Classes demonstrating method overriding:
 class C {
     A* getFoo() {
         return new A();
     }
 }

 class D : public C {
     B* getFoo() {
         return new B();
     }
 }

Таким образом устраняется необходимость в кастинге.

0 голосов
/ 28 сентября 2010

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

Если вам понадобитсячасто опускать руки, чтобы получить доступ к специфическим функциям / данным детского класса, этот подход, вероятно, не оптимален для вашей ситуации.В этом случае вы можете написать некоторые функциональные возможности вне классов, предоставить несколько реализаций для каждого типа и использовать какой-то RTTI, чтобы при необходимости уменьшить значение.Это более грязно, но, как правило, чаще встречается за пределами «академического» или хорошо изолированного использования.

Похоже, у вас есть много полезной информации / советов в других ответах.

0 голосов
/ 28 сентября 2010

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

...