Может ли базовый класс знать, переопределил ли производный класс виртуальный метод? - PullRequest
0 голосов
/ 17 мая 2018

Тот же вопрос существует для 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 голосов
/ 17 мая 2018

ПРЕДУПРЕЖДЕНИЕ : Это решение не является кроссплатформенным, так как оно опирается на расширение GCC и некоторое неопределенное поведение.

GCC позволяет синтаксису получить указатель на функцию из таблицы this, сказав this->*&ClassName::functionName. Вероятно, не стоит использовать это на самом деле, но в любом случае вот демо:

#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 полностью отклоняют код.
0 голосов
/ 25 мая 2018

Это проблема дизайна.

Однако, в интересах ответа на реальный вопрос, есть несколько способов, которыми вы могли бы сделать это без редизайна (но на самом деле, вам следует изменить его).

Один (ужасный) вариант - вызвать метод 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"; }

Это, по крайней мере, движется в направлении лучшего дизайна. Смысл наследования заключается в том, чтобы позволить высокоуровневому коду использовать объекты взаимозаменяемо, независимо от их специализации. Если диспетчер должен проверить фактический тип объекта, то вы нарушили принцип подстановки Лискова. Диспетчер должен иметь возможность обрабатывать все объекты одинаково, и любые различия в поведении должны возникать из-за самих переопределенных методов (а не из-за наличия переопределений).

0 голосов
/ 17 мая 2018

Если все упростить, решение об отправке принимается классом 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"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...