чисто виртуальная функция с реализацией в C ++ - PullRequest
0 голосов
/ 02 марта 2019

Я знаю, что в базовом классе можно реализовать реализацию чисто виртуальной функции в качестве реализации по умолчанию.Но я не совсем понимаю код, приведенный ниже.

class A {
public:
    virtual void f1() = 0;
    virtual void f2() = 0;
};
void A::f1(){
    cout << "base f1" << endl;
    f2();
}
void A::f2(){
    cout << "base f2" << endl;
}

class B: public A {
public:
    void f1() { A::f1(); }
    void f2() { cout << "derived f2" << endl; }
};

int main(){
    B b;
    b.f1();
}

Почему B :: f1 () вызывает B :: f2 () вместо A :: f2.Я знаю, что так будет вести себя, но почему? какие базовые знания я пропустил.

другой вопрос, сделала ли реализация чистой виртуальной функции в базовом классе чистой (= 0) ненужной?

1 Ответ

0 голосов
/ 02 марта 2019

Это поведение, которое стандарт C ++ определяет для виртуальных функций: вызов версии самого доступного производного типа.

Конечно, для нормальных объектов самый производный тип - это тип самого объекта:

B b;
b.f1(); // of course calls B's version

Интересная часть, если у вас есть указатели или ссылки:

B b;
A& ar = b;
A* ap = &b;

// now both times, B's version will be called
ar.f1();
ap->f1();

То же самое происходит внутри f1, фактически вы делаете неявно:

this->f2(); // 'this' is a POINTER of type A* (or A const* in const functions).

Существует явление, когда этого не происходит (в приведенном ниже примере требуется конструктор копирования):

B b;
A a = b; // notice: not a pointer or reference!
A.f1();  // now calls A's version

В действительности здесь происходит только то, что копируется только часть A из bв a и часть B отбрасывается, поэтому a на самом деле является истинным, не производным A объектом.Это называется «разрезанием объектов» и является причиной того, что вы не можете использовать базовые объекты, например, в std::vector для хранения полиморфных объектов, но вместо этого нужны указатели или ссылки.


Вернуться к виртуальным функциям:Если вас интересуют технические детали, это решается с помощью виртуальных таблиц функций, коротких таблиц.Имейте в виду, что это только де-факто стандарт, C ++ не требует реализации через vtables (и фактически, другие языки, поддерживающие полиморфизм / наследование, такие как Java или Python, также реализуют vtables).

Для каждогоВиртуальная функция в классе есть запись в соответствующей таблице.

Нормальные функции вызываются напрямую (т.е. выполняется безусловный переход к адресу функции).Для вызова виртуальных функций, напротив, сначала нужно найти адрес в виртуальной таблице, и только затем мы можем перейти к адресу, хранящемуся там.

Производные классы теперь копируют виртуальные таблицы своих базовых классов (поэтому изначально онисодержат те же адреса, что и таблицы базового класса), но замените соответствующие адреса, как только вы переопределите функцию.

Кстати: вы можете указать компилятору не использовать vtable, а явно вызвать определенныйвариант:

B b;
A& a = b;
a.A::f1(); // calls A's version inspite of being virtual,
           // because you explicitly told so
b.A::f1(); // alike, works even on derived type
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...