Подтвердить что ты не робот

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

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

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

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

Вот простая реализация factory, которая заставляет метод factory для производного класса.

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"

Ответ 2

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

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).

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

Ответ 3

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

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

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