статические абстрактные методы в с ++ - PullRequest
19 голосов
/ 23 июля 2010

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

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

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

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

Есть ли известная идиома для этого? Или я просто оставлю это в комментариях? Может быть, я не должен пытаться заставить это все равно

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

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

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

Ответы [ 3 ]

33 голосов
/ 23 июля 2010

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

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

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

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<TClass, TInterface> производный класс вынужден иметь метод CreateInternal компилятором.Не определив его, вы получите ошибку вроде этой:

ошибка C2039: «CreateInternal»: не является членом «Thingy»

0 голосов
/ 23 июля 2010

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

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

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

0 голосов
/ 23 июля 2010

Нет точного способа прописать такой контракт в 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<their_class_name> (вы можете применить это с настройкой видимости конструктора, но вы не можете гарантировать, что клиенты не лгут о their_class_name).

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

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