Как вы вызываете виртуальный метод, хранящийся в базовом классе и вызываемый классом, который наследует этот базовый класс дважды? - PullRequest
0 голосов
/ 08 марта 2019

Этот код демонстрирует проблему:

class Base
{
public:
    explicit Base(std::function<void()> const& printFunc) :
        _printFunc(printFunc)
    {
    }

    void print()
    {
        _printFunc();
    }
private:
   std::function<void()> _printFunc{};
private:
    virtual void _print() = 0; // If this line is commented out, then 
                               // `Subclass1::_print()` can be called.
};

class Subclass1 : public Base
{
public:
    explicit Subclass1() :
        Base([this]() { _print(); })
    {
    }
private:
    void _print() /*override*/
    {
        std::cout << "Subclass1\n";
    }
};

class Subclass2 : public Base, public Subclass1
{
public:
    using fromLowestSubclass = Base;
public:
    explicit Subclass2() :
        Base([this]() { _print(); }), Subclass1()
    {
    }
private:
    void _print() /*override*/
    {
        // Here is the problem:
        Subclass1::print(); // or: static_cast<Subclass1*>(this)->print(); 

        std::cout << "Subclass2\n";
    }
};

int main()
{
    Subclass2 sc2{};
    sc2.fromLowestSubclass::print();

    return 0;
}

В методе Subclass2::_print должен быть вызван переопределяющий метод _print для Subclass1, но вместо этого оператор Subclass1::print(); вызывает текущий методснова.Эту проблему можно предотвратить, если закомментировать оператор virtual void _print() = 0;.
Почему использование виртуального метода _print не позволяет мне вызывать перегруженный виртуальный метод Subclass1::_print и какое решение существует, чтобы мне не пришлосьобойтись без виртуальных методов?

1 Ответ

0 голосов
/ 08 марта 2019
class Base
{
    ....
private:
    virtual void _print() = 0; 
}

Это означает: вы можете переопределить _print, но не можете его назвать, только Base имеет право вызывать его.

Сейчас:

class Base
{
public:
    void print()
    {
        _printFunc();
    }

делает это, вызывает _printFunc как виртуальную функцию, которая соответствует текущей реализации объекта.Он не измеряет, как был вызван print().

Добавление Subclass1:: в качестве префикса просто меняет область имен и не влияет на поведение метода.Это оказывает влияние только на область имен.

Теперь, если виртуальный метод имеет такой префикс, то выбор области действия имени указывает компилятору, что вам следует отказаться от абстракции, и вам нужно вызвать определенный метод.В этом случае метод вызывается без обращения к виртуальной таблице.

Двойное наследование не влияет на эту проблему.

Вы можете предоставить вспомогательный метод, который вы сможете вызывать от предка:

class Subclass1 : public Base
{
    ....
protected:
    void sub1_print() // not virtual
    {
        std::cout << "Subclass1\n";
    }
private:
    void _print() /*override*/
    {
        sub1_print();
    }
};

class Subclass2 : public Base, public Subclass1
{
    ....
private:
    void _print() /*override*/
    {
        sub1_print();

        std::cout << "Subclass2\n";
    }
};
...