Используя силу виртуальных функций - PullRequest
1 голос
/ 14 декабря 2011

Рассмотрим следующий пример кода:

class Base {
public:
    void f();
    virtual void vf();
};

class Derived : public Base {
public:
    void f();
    void vf();
};

#include <iostream>
using namespace std;

void Base::f() {
    cout << "Base f()" << endl;
}

void Base::vf() {
    cout << "Base vf()" << endl;
}

void Derived::f() {
    cout << "Derived f()" << endl;
}

void Derived::vf() {
    cout << "Derived vf()" << endl;
}

int main()
{
    Base b1;
    Derived d1;
    b1.f();
    b1.vf();
    d1.f();
    d1.vf();

    Derived d2;     // Derived object
    Base* bp = &d2; // Base pointer to Derived object
    bp->f();    // Base f()
    bp->vf();   // which vf()?

    return 0;
}

Результат прогона:

Base f()

Base vf()

Derived f()

Derived vf()

Base f()

Derived vf()

Вопросы:

  1. В строке Base* bp = &d2 тип объекта известен во время компиляции. Тогда решение какой функции использовать в случае bp->vf(); также может быть принято во время компиляции, верно?

  2. Поскольку тип объекта известен во время компиляции, используется ли мощность виртуальных функций в этом примере программы?

Ответы [ 4 ]

4 голосов
/ 14 декабря 2011

В строке Base* bp = &d2 тип объекта известен во время компиляции.Тогда решение о том, какую функцию использовать в случае bp->vf();, также может быть принято во время компиляции, верно?

Да.
Это часть оптимизации компилятора, которая наиболее современна для умных целей.компиляторы смогут это сделать.

Если компилятор может определить, какую функцию вызывать во время компиляции, он это сделает.Хотя это полностью зависит от компилятора, может ли он определить точную функцию, вызываемую во время компиляции или во время выполнения, виртуализм гарантирует вам поведение, которое вы хотите для своей программы.

Поскольку тип объекта известен всамо время компиляции, используется ли мощность виртуальных функций в этом примере программы?

полностью зависит от компилятора.Большинство современных дневных компиляторов смогут оценить этот вызов функции во время компиляции.

3 голосов
/ 14 декабря 2011

Эта программа тривиальна и действительно не очень хорошо демонстрирует силу виртуальных функций (или, в более общем смысле, полиморфизм). Учтите это изменение:

// add second derived class
class Derived2 : public Base {
public:
    void vf() { std::cout << "Derived2 vf()" << std::endl; }
};

// in main
int user_choice;
std::cin >> user_choice;
Base * ptr;
if (user_choice == 0)
    ptr = new Derived();
else
    ptr = new Derived2();
ptr->vf();

Здесь выбор класса зависит от пользовательского ввода - компилятор не может предсказать, какой тип объекта ptr будет фактически указывать при достижении вызова ptr->vf();.

1 голос
/ 14 декабря 2011

In the line Base* bp = &d2, the object type is known at compile time. Then the decision of which function to use in the case of bp->vf(); can also be made at compile time right?

Нет, решение о том, какую функцию использовать, принимается динамически во время выполнения в зависимости от типа объекта, а не от типа указателя / ссылки на этот объект.Это называется динамическим связыванием.Компилятор хранит скрытый указатель с именем virtual pointer или vptr, который указывает на таблицу, называемую виртуальной таблицей.В каждом классе будет одна виртуальная таблица, по крайней мере, одна виртуальная функция (независимо от того, сколько объектов создано для класса).Виртуальная таблица содержит адрес виртуальной функции класса.

Since the object type is known at compile time itself, is the power of virtual functions used in this sample program?

Фактически указанный объект может быть неизвестен во время компиляции.Возьмем пример метода, который принимает указатель базового класса в качестве параметра, как показано ниже:

void draw(Shape *bp)
{
    bp->draw();
}

В этом случае фактическим объектом может быть любая форма, полученная из Shape.Но нарисованная форма зависит от фактического типа переданного объекта.

1 голос
/ 14 декабря 2011

Ну ... ДА и НЕТ на оба вопроса: это зависит от того, на каком уровне абстракции вы спорите.

  • С языковой точки зрения 1) является заблуждением. Тип для d2 известен, но когда ему присваивается адрес d2 для bp, произойдет преобразование из Derived* в Base*. С тех пор статический тип bp равен Base* (потому что это его объявление), а динамический тип указывает на его Derived (потому что это то, к чему относится информация типа времени выполнения, связанная с объектом). Начиная с них, каждая операция через bp принимает Base * как тип и требует перенаправления для каждой виртуальной функции.

  • С точки зрения компилятора может быть выполнена определенная оптимизация, и, поскольку во всех ваших функциях ваш pb всегда указывает на Derived, виртуальное перенаправление может быть фактически пропущено. Но это связано с тем, как ваш конкретный пример структурирован, а не с языковой особенностью.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...