Соответствие функций полиморфизма C++

У меня есть код ниже с цепным наследованием с виртуальной функцией, и он печатает:

D ptr
E

Итак, каков алгоритм для сопоставления функций, я полагаю, что он соответствует ближайшему классу отца? Но &e в основном имеет тип A*,B*, D * почему это даже компилируется и является ли хорошей практикой использовать это свойство?

#include <iostream>

using namespace std;

class A {
    public:
        virtual void f(){cout<<"A";};
};

class B :public A {
    public:
    virtual void f() {
        cout<<"B";
    }
};

class D : public B {
    public:
    virtual void f() {
        cout<<"D";
    }
};

class E : public D {
    public:
    virtual void f() {
        cout<<"E";
    }
};

void f(D *sth) {
    cout<<"D ptr"<<endl;
    sth->f();
}

void f(B *sth) {
    cout<<"B ptr"<<endl;
    sth->f();
}

void f(A *sth) {
    cout<<"A ptr"<<endl;
    sth->f();
}

int main() {
    E e;
    f(&e);
}

2 ответа

  1. Я постараюсь разбить ваш пример как можно лучше:

    int main() {
        E e;    // declare object of Class E
        f(&e);  // run f() function and pass it the address of E
    }
    

    ОК достаточно просто-вы объявили объект класса E, а затем запустили функцию на нем. Теперь глобальная функция f () была перегружена три раза, чтобы принять указатели на адрес объекта D, объекта B или объекта. Так как все они принимают объекты выше в дереве наследования, любой будет работать для объекта класса E. Компилятор выберет функцию для наиболее производного класса. В этом случае выбирается функция void f(D *sth).

    void f(D *sth) {
        cout<<"D ptr"<<endl; // "D ptr" printed to screen
        sth->f();            // Take original pointer to class E object and 
                             // run its own class method f() on it
    }
    

    Затем, глядя на определение класса для E, мы увидим, почему e печатается:

    class E : public D {
        public:
        virtual void f() {
            cout<<"E";
        }
    };
    

    Если вы должны были прокомментировать функцию void f(D *sth), компилятор вместо этого выбрал бы функцию void f (B *sth), в результате чего выходные данные «B ptr» и «E». Если вместо этого вы закомментируете глобальную функцию f () для объектов A и B, а затем попытаетесь запустить void f (D *sth) для объекта B, он не будет компилироваться, потому что компилятор может преобразовывать объекты только вверх по дереву наследования, а не вниз по нему.

    Как прокомментировал Phil1970, эта структура обычно не одобряется, поскольку она опирается на глобальные функции, имеющие знание дерева наследования.

  2. Это должно было дать вам неопределенное поведение или двусмысленность. Разные версии компилятора могут давать разные результаты, поэтому лучше не полагаться на результаты 🙂