статические абстрактные методы в C++

У меня есть абстрактный базовый класс

class IThingy
{
  virtual void method1() = 0;
  virtual void method2() = 0;
};

Я хочу сказать - "все классы, обеспечивающие конкретное инстанцирование, должен обеспечить эти статические методы также"

Я испытываю желание сделать

class IThingy
{
  virtual void method1() = 0;
  virtual void method2() = 0;
  static virtual IThingy Factory() = 0;
};

Я знаю, что это не компилирует, и так или иначе не ясный, как использовать его, даже если это действительно компилировало. И так или иначе я могу просто сделать

Concrete::Factory(); // concrete is implementation of ITHingy

не упоминая Фабрику в базовом классе вообще.

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

Существует ли известная идиома для этого? Или я просто помещаю его в комментарии? Возможно, я не должен пытаться вызвать это так или иначе

Править: Я мог чувствовать, что я был неопределенен, когда я ввел вопрос. Я просто чувствовал, что должен быть некоторый способ выразить это. Igor дает изящный ответ, но на самом деле он показывает, что действительно не помогает. Я все еще заканчиваю тем, что имел необходимость сделать

   IThingy *p;
    if(..)
       p = new Cl1();
    else if(..)
       p = new Cl2();
    else if(..)
       p = new Cl3();
    etc.

Я предполагаю отражающие языки как c#, Python или Java могли предложить лучшее решение

21
задан pm100 23 July 2010 в 01:03
поделиться

3 ответа

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

Вместо этого вы можете вынести создание из интерфейса (желаемый виртуальный статический метод) и поместить его в класс-фабрику.

Вот простая реализация фабрики, которая заставляет использовать фабричный метод в производном классе.

template <class TClass, class TInterface>
class Factory {
public:
    static TInterface* Create(){return TClass::CreateInternal();}
};

struct IThingy {
    virtual void Method1() = 0;
};

class Thingy : 
    public Factory<Thingy, IThingy>,
    public IThingy {
        //Note the private constructor, forces creation through a factory method
        Thingy(){}
public:
        virtual void Method1(){}
        //Actual factory method that performs work.
        static Thingy* CreateInternal() {return new Thingy();}
};

Использование:

//Thingy thingy; //error C2248: 'Thingy::Thingy' : cannot access private member declared in class 'Thingy'

IThingy* ithingy = Thingy::Create(); //OK

При выводе из Factory производный класс вынужден иметь метод CreateInternal. Отсутствие его определения приведет к ошибке, подобной этой:

error C2039: 'CreateInternal' : is not членом 'Thingy'

32
ответ дан 29 November 2019 в 21:17
поделиться

Нет надежного способа прописать такой контракт в C ++, поскольку также нет способа использовать этот вид полиморфизма, поскольку строка

Concrete::Factory()

всегда статическая вещь времени компиляции , то есть вы не можете написать эту строку, где Concrete будет еще неизвестным классом, предоставляемым клиентом.

Вы можете заставить клиентов реализовывать такой «контракт», сделав его более удобным, чем не предоставляя его. Например, вы можете использовать CRTP:

class IThingy {...};

template <class Derived>
class AThingy : public IThingy
{
public:
  AThingy() { &Derived::Factory; } // this will fail if there is no Derived::Factory
};

и сообщить клиентам, что они производны от AThingy (вы можете добиться этого с помощью настройки видимости конструктора, но вы не можете гарантировать, что клиенты не лгут о имя_класса ).

Или вы можете использовать классическое решение, создать отдельную иерархию фабричных классов и попросить клиентов предоставить свой объект ConcreteFactory вашему API.

0
ответ дан 29 November 2019 в 21:17
поделиться

Статические методы нельзя сделать виртуальными (или абстрактными, если на то пошло) в C ++.

Чтобы сделать то, что вы намереваетесь, у вас может быть метод IThingy :: factory , который возвращает конкретный экземпляр, но вам нужно каким-то образом предоставить фабрике средства для создания этого экземпляра. Например, определите сигнатуру метода, такую ​​как IThing * (thingy_constructor *) () , и укажите статический вызов в IThingy , которому вы можете передать такую ​​функцию, которая определяет, как IThingy создаст экземпляр фабрики. Затем в зависимой библиотеке или классе вы можете вызвать этот метод с соответствующей функцией, которая, в свою очередь, теперь показывает, как правильно сконструировать объект, реализующий ваш интерфейс.

Предположим, у вас не был вызван «инициализатор» фабрики, вы бы хотели предпринять соответствующие действия, например, выбросить исключение.

0
ответ дан 29 November 2019 в 21:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: