基类是否可以知道派生类是否覆盖了虚方法?

时间:2018-05-17 00:11:30

标签: c++ c++11

C#存在同样的问题,但不适用于C ++。

class Base
{
    void dispatch()
    {
        if (newStyleHasBeenOverridden())   //how to find this out?
            newStyle(42);
        else
            oldStyle(1, 2);
    }

    virtual void oldStyle(int, int) { throw "Implement me!"; }
    virtual void newStyle(int) { throw "Implement me!"; }
}

class Derived:public Base
{
    void newStyle(int) override
    {
        std::cout<<"Success!";
    }
}

3 个答案:

答案 0 :(得分:2)

警告:此解决方案不是跨平台的,因为它依赖于GCC扩展和一些未定义的行为。

GCC允许语法通过说thisthis->*&ClassName::functionName的vtable中获取指向该函数的指针。实际使用它可能不是一个好主意,但无论如何这是一个演示:

#include <iostream>

class Base {
public:
    void foo() {
        auto base_bar_addr = reinterpret_cast<void*>(&Base::bar);
        auto this_bar_addr = reinterpret_cast<void*>(this->*&Base::bar);
        std::cout << (base_bar_addr == this_bar_addr ? "not overridden" : "overridden") << std::endl;
    }

    virtual void bar() { };
};

class Regular : public Base { };

class Overriding : public Base {
public:
    virtual void bar() { };
};

int main() {
    Regular r;
    r.foo();
    Overriding o;
    o.foo();
}

后人:

  • ICC允许使用语法,但它有不同的含义,就像只说&Base::bar一样,所以你总是认为它没有被覆盖。
  • Clang和MSVC完全拒绝该代码。

答案 1 :(得分:2)

这是一个设计问题。

然而,为了回答实际问题,有几种方法可以在不重新设计的情况下完成此任务(但实际上,重新设计它)。

一个(可怕的)选项是调用newstyle方法并捕获未被覆盖的异常。

void dispatch() {
    try {
        newStyle(42);
    } catch (const char *) {
        oldStyle(1, 2);
    }
}

如果覆盖了newStyle,则会调用覆盖。否则,基本实现将抛出,哪个调度将捕获然后回退到oldStyle。这是对例外的滥用,而且表现不佳。

另一种(稍微不那么糟糕)的方法是将newStyle的基本实现转发到oldStyle。

void dispatch() {
    newStyle(42);
}

virtual void newStyle(int) { oldStyle(1, 2); }
virtual void oldStyle(int, int) { throw "implement me"; }

这至少会朝着更好的设计方向发展。继承点是允许高级代码能够交替使用对象,而不管它们的专业化程度如何。如果调度必须检查实际的对象类型,那么您违反了Liskov替换原则。 Dispatch应该能够以相同的方式处理所有对象,并且行为的任何差异都应该来自被覆盖的方法本身(而不是覆盖的存在)。

答案 2 :(得分:0)

使事情更简单,调度决定由Derived类完成。
抽象Base类基本上只是一个“接口”,其中Derived实施所有 virtual功能。

这个问题听起来像XY问题。

我认为这就是你想要的:

class Base // abstract class
{
   virtual void oldStyle(int, int) = 0; // pure virtual functions
   virtual void newStyle(int) = 0;      // needs to be implemented
};

class Derived:public Base
{
   public:
      Derived(bool useNewStyle): _useNewStyle(useNewStyle) {}
      void newStyle(int) { std::cout << "new style"; }
      void oldStyle(int, int) { std::cout << "old style"; }
      void dispatch()
      {
         if (_useNewStyle) {
            newStyle(42);
            return;
         }
         oldStyle(1, 2);
         return;
      }

   private:
      bool _useNewStyle = false;
};

Derived d(true); // use new style
d.dispatch();    // "new style"