如何强制所有派生类实现虚方法?

时间:2011-09-02 20:11:14

标签: c++ inheritance enforcement

假设您有一个类树的基类Dep。我希望每个叶子类都实现一个虚方法Dep* Dep::create()。有没有办法强制执行此操作?

注意:这里的问题是可能有中间类(比如class B : public A : public Dep)意外地实现了这个方法(A::create),或者因为他们认为他们是叶子类,但实际上是自己的子类。

问题在此结束。

上下文

如果你很好奇为什么我需要这个;我有一个类Master,它有Dep个未知具体类型的对象。如果重复Master,我需要找到Dep实例的匹配克隆。接下来要做的最好的事情是虚拟构造函数idiom,它精确地引入了这个问题。

此外,我甚至无法抓住这一点(其他因为可怕的崩溃),因为出于晦涩的原因,比我更有说服力的人在这个项目中取缔dynamic_cast(也许这是一个好的决定;但无论如何都是一个完全不同的讨论。)

5 个答案:

答案 0 :(得分:3)

C ++没有办法让类继承自类,并且无法在继承层次结构中使特定的类实现方法。唯一的规则是在特定类(不一定在叶子中)的继承层次结构中某处所有虚函数必须具有该类可实例化的实现。

例如,A可以从Def继承并实现它的所有[纯]虚方法。然后,如果B继承自A,则不必实现任何内容。没有办法阻止这种情况发生。

所以答案是否定的,没有办法强制执行。

答案 1 :(得分:3)

使用奇怪的重复模板乐趣,你可以实现非常相似的东西:

template<typename T>
class Cloneable : public T, public Dep
{
private:
    Cloneable<T>() : T() { }
public:
    static Cloneable<T>* Create() { return new Cloneable<T>(); }
    Cloneable<T>* clone() { return new Cloneable<T>(*this); }
};

使用Dep,而不是从new MyType派生并通过Cloneable<MyType>::Create实例化。由于Cloneable<MyType>派生自MyType,因此您可以像使用任何MyType一样使用该实例,但现在保证它具有Dep::clone

此外,您的Master不应接受Dep类型的实例,但强制它是Cloneable<T>。 (用强制执行此操作的简单函数模板替换您的orignial函数。)这可以保证master中的任何Dep都具有正确实现的clone函数。

由于Cloneable<MyType>没有公共构造函数,因此无法继承它,但您的实际MyType可以像以前一样进一步继承和使用。

答案 2 :(得分:2)

TPTB是否取缔所有 RTTI,或仅dynamic_cast<>()?如果你可以使用RTTI,那么你可以强制该方法的存在作为调用它的后置条件:

#include <typeinfo>
#include <cassert>
#include <iostream>
#include <stdexcept>

class Base {
protected:
  virtual Base* do_create() = 0;
  virtual ~Base() {}
public:
  Base* create() {
    Base *that = this->do_create();
    if( typeid(*this) != typeid(*that) ) {
      throw(std::logic_error(std::string() +
                             "Type: " +
                             typeid(*this).name() +
                             " != " +
                             typeid(*that).name()));
    }
    return that;
  }
};

class Derive1 : public Base {
protected:
  Base* do_create() { return new Derive1(*this); }
};

class Derive2 : public Derive1 {};

void f(Base*p) { std::cout << typeid(*p).name() << "\n"; }
int main() {
  Derive1 d1;
  Base *pD1 = d1.create(); // will succeed with correct semantics
  Derive2 d2;
  Base *pD2 = d2.create(); // will throw exception due to missing Derive2::do_create()
}

答案 3 :(得分:2)

如果您控制基类AbstractDep,则可以强制执行必须使用类模板WithCloning创建具体叶类。然后可以密封该叶子,使其不能被遗传。或者更准确地说,不能创建派生类的实例。

class AbstractDep
{
template< class Type > friend class WithCloning;
private:
    enum FooFoo {};
    virtual FooFoo toBeImplementedByLeafClass() = 0;

public:
    virtual AbstractDep* clone() const = 0;
};

template< class Type > class WithCloning;

class Sealed
{
template< class Type > friend class WithCloning;
private:
    Sealed() {}
};

template< class Type >
class WithCloning
    : public Type
    , public virtual Sealed
{
private:
    AbstractDep::FooFoo toBeImplementedByLeafClass()
    {
        return AbstractDep::FooFoo();
    }

public:
    virtual WithCloning* clone() const
    {
        return new WithCloning( *this );
    }
};

typedef WithCloning<AbstractDep>            Dep;


class AbstractDerivedDep
    : public AbstractDep
{
// AbstractDep::FooFoo toBeImplementedByLeafClass(); // !Not compile.
public:
};

typedef WithCloning<AbstractDerivedDep>     DDep;


struct Foo: Dep {};       // !Does not compile if instantiated.

int main()
{
    Dep     d;
    //Foo     f;
}

如果类需要的不仅仅是默认构造,那么大多数都需要另外解决。

然后一个解决方案是从WithCloning构造函数转发一个参数包(我的博客上有一个示例C ++ 98实现,而C ++ 0x直接支持它。)

总结一下,要实例化,该类必须是WithCloning

干杯&amp;第h。,

答案 4 :(得分:0)

当你说它们不为人知时,我认为它们仍然从公共基类/接口继承?

只有我能想到你可以使用强制在虚拟基类上添加

virtual Base* clone() {
    assert( 1==0 ); //needs to be overriden
}

因此您被迫覆盖,但只有在尝试在缺少覆盖的类的实例上调用clone时才会在运行时检测到

即使您不允许使用dynamic_cast或RTTI,您仍然可以在构建本地启用它以进行调试,如果这样可以帮助您找到违规类的类型

听起来你熟悉克隆模式,但我会在这里安静地发布它,我们可以忘掉它: http://www.cplusplus.com/forum/articles/18757/