7

在 C++ 中,为什么纯方法 virtual强制其直接子代(用于对象创建)而不是大子代等强制覆盖?

struct B {
  virtual void foo () = 0;
};
struct D : B {
  virtual void foo () { ... };
};

struct DD : D {
  // ok! ... if 'B::foo' is not overridden; it will use 'D::foo' implicitly
};

我认为忽略此功能没有什么大不了的。
例如,从语言设计的角度来看,struct DD只有D::foo当它具有像using D::foo;. 否则它必须覆盖foo强制。

有没有在 C++ 中产生这种效果的实用方法?

4

5 回答 5

8

我发现了一种机制,至少我们被提示显式地宣布被覆盖的方法。虽然这不是完美的方式。

假设,我们virtual在 base 中有几个纯方法class B

class B {
  virtual void foo () = 0;
  virtual void bar (int) = 0;
};

其中,假设我们只想foo()被整个层次结构覆盖。为简单起见,我们必须有一个virtual包含特定方法的基类。它有一个模板构造函数,它只接受与该方法相同的类型。

class Register_foo {
  virtual void foo () = 0; // declare here
  template<typename T>  // this matches the signature of 'foo'
  Register_foo (void (T::*)()) {}
};
class B : public virtual Register_foo {  // <---- virtual inheritance
  virtual void bar (int) = 0;
  Base () : Register_foo(&Base::foo) {}  // <--- explicitly pass the function name
};

层次结构中的每个后续子类都必须在其每个构造函数中显式注册a 。例如:foo

struct D : B {
  D () : Register_foo(&D::foo) {}
  virtual void foo () {};
};

这种注册机制与业务逻辑无关。虽然,孩子class可以选择使用自己的foo或父母的foo甚至一些类似的语法方法进行注册,但至少这是明确宣布的。

于 2012-03-12T04:00:35.970 回答
2

在你的例子中,你没有宣布D::foo纯洁;这就是为什么它不需要被覆盖。如果你想要求它再次被覆盖,那么声明它是纯的。

如果您希望能够实例化D,但强制任何进一步的派生类覆盖foo,那么您不能。但是,您可以从中派生另一个类D,重新声明它是纯的,然后从该类派生的类必须再次覆盖它。

于 2012-02-28T07:07:33.360 回答
2

您基本上要求的是要求最派生的类实现该功能。我的问题是:为什么?我能想象到的唯一一次与此相关的是类似clone()or 的函数another(),它返回相同类型的新实例。这就是你真正想要强制执行的,新实例具有相同的类型;即使在那里,实际实现该功能的位置也无关紧要。你可以强制执行:

class Base
{
    virtual Base* doClone() const = 0;
public:
    Base* clone() const
    {
        Base* results = doClone();
        assert( typeid(*results) == typeid(*this) );
        return results;
    }
}

(在实践中,我从来没有发现人们忘记覆盖clone是一个真正的问题,所以我从来没有为上面的事情烦恼过。这是一种普遍有用的技术,但是,只要你想强制执行后置条件。)

于 2012-02-28T08:52:32.900 回答
1

纯虚拟意味着要被实例化,纯虚拟必须在声明纯虚函数的类的某些后代中被覆盖。这可以在被实例化的类或声明纯虚拟的基类和被实例化的基类之间的任何中间类中。

但是,仍然有可能拥有从具有纯虚拟的类派生的中间类,而无需覆盖该纯虚拟。与声明纯虚的类一样,这些类只能用作基础类;您不能创建这些类的实例,只能创建从它们派生的类,其中每个纯虚拟都已实现。

至于要求后代覆盖虚拟,即使中间类已经这样做了,答案是否定的,C++ 没有提供任何至少打算这样做的东西。似乎您可以使用多个(可能是虚拟的)继承来共同破解某些东西,因此中间类中的实现会存在,但尝试使用它会是模棱两可的,但我认为这不足以确定它如何(或是否)工作——即使它工作了,它也只会在尝试调用相关函数时发挥作用,而不仅仅是实例化一个对象。

于 2012-02-28T07:18:50.683 回答
1

有没有在 C++ 中产生这种效果的实用方法?

不,而且有充分的理由。如果这是标准的一部分,想象一下大型项目中的维护。一些基类或中间基类需要添加一些公共接口,一个抽象接口。现在,它的每个子孙都需要更改和重新编译(即使它像您建议的那样使用 D::foo() 添加简单),您可能会看到它的标题,地狱厨房。

如果您真的想强制实施,您可以强制在子类中实施一些其他纯虚拟。这也可以使用 CRTP 模式来完成。

于 2012-03-09T21:01:50.310 回答