C++中的静态抽象方法

static abstract methods in c++

我有一个抽象基类

1
2
3
4
5
class IThingy
{
  virtual void method1() = 0;
  virtual void method2() = 0;
};

我想说:"提供具体实例的所有类也必须提供这些静态方法。"

我很想去做

1
2
3
4
5
6
class IThingy
{
  virtual void method1() = 0;
  virtual void method2() = 0;
  static virtual IThingy Factory() = 0;
};

我知道这不会编译,而且即使它确实编译了,也不清楚如何使用它。不管怎样,我能做的就是

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

根本没有提到基类中的工厂。

但我觉得应该有某种方式来表达我希望实现能够签署的契约。

这有一个众所周知的成语吗?或者我只是把它写在评论里?也许我不应该强迫你这么做

编辑:当我输入问题时,我会觉得自己很含糊。我只是觉得应该有办法表达它。伊戈尔给出了一个优雅的答案,但事实上,这表明它确实没有帮助。我最终还是不得不这么做

1
2
3
4
5
6
7
8
   IThingy *p;
    if(..)
       p = new Cl1();
    else if(..)
       p = new Cl2();
    else if(..)
       p = new Cl3();
    etc.

我猜像C语言、Python或Java这样的反射语言可以提供更好的解决方案。


你所面临的问题部分与轻微违反单一责任原则有关。您试图通过接口强制创建对象。相反,接口应该更纯粹,并且只包含与接口应该做的事情成整数的方法。

相反,您可以将创建从接口(所需的virtual static方法)中取出,并将其放入工厂类中。

这里是一个简单的工厂实现,它强制派生类上的工厂方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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();}
};

用途:

1
2
3
//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
a member of 'Thingy'


静态方法不能在C++中虚拟化(或抽象化)。

要想做你想做的,你可以有一个返回具体实例的IThingy::factory方法,但是你需要以某种方式为工厂提供创建实例的方法。例如,定义一个像IThing* (thingy_constructor*)()这样的方法签名,在IThingy中有一个静态调用,您可以将这样的函数传递给它,它定义了IThingy将如何构造工厂实例。然后,在依赖库或类中,您可以使用适当的函数调用此方法,该函数反过来又知道如何正确地构造实现接口的对象。

假设您还没有调用工厂的"初始值设定项",您将希望采取适当的操作,例如引发异常。


在C++中没有这样的约定,因为也没有办法使用这种多态性。

1
Concrete::Factory()

总是静态编译时的事情,也就是说,您不能在Concrete将是一个未知的客户端提供的类的地方编写此行。

你可以让客户执行这种"契约",使它比不提供它更方便。例如,您可以使用crtp:

1
2
3
4
5
6
7
8
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说谎)。

或者您可以使用经典的解决方案,创建工厂类的单独层次结构,并要求客户机向您的API提供他们的ConcreteFactory对象。