Вызов виртуальной функции базового класса из производного конструктора - PullRequest
0 голосов
/ 16 ноября 2018

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

class Base
{
    public:
    virtual void fun()
    {
        cout<<"In Base"<<endl;
    }
};

class Derived : public Base
{
    public:
    Derived(Base obj)
    {
        Base *p;
        p = &obj;
        p->fun();
    }
    void fun()
    {
        cout<<"In Derived"<<endl;
    }
};

int main()
{
    Base b;
    Derived d(b);
}

O / P: В базе

Я сомневаюсь, что компилятор может использовать динамическое связывание вместо раннего. Пожалуйста, исправьте меня, я ошибаюсь, но мне нужно четкое объяснение того, как компилятор выполняет раннее связывание внутри конструктора?

Если метод не является конструктором, компилятор преобразует этот оператор p-> fun в что-то вроде

fetch the VPTR at run time (This vptr will have the derived class VTABLE address)

invoke method at vptr + offset 

hence the right virtual function is invoked.

но, поскольку он является конструктором, компилятор останавливает рассмотрение виртуального механизма и просто забывает о типе указателя и заменяет вышеприведенный оператор p-> fun () на obj.fun () (внутренне обрабатывая его как вызов соответствующим объект)

Потому что даже если вы делаете раннее или позднее связывание, результат остается тем же самым (вызывая local fun ())

Также, если это локальная версия функции, которая вызывается в соответствии с механизмом раннего связывания внутри конструктора, то как мы вызываем базовую версию функции, которая не является локальной для производной (я понимаю, что производная содержит базовую подпрограмму) объект, но необходимо знать причину вызова базовой версии, а не локальной / производной функции, если конструктор выполняет раннее связывание внутри конструктора для виртуальных функций)

Пожалуйста, помогите

Ответы [ 2 ]

0 голосов
/ 16 ноября 2018

В вашем примере конструктор Derived вызывает метод fun() объекта obj типа Base, который вы передали в качестве параметра Derived::Derived. Это не имеет ничего общего с экземпляром Derived, который вы создаете, поэтому вы просто получаете вызов Base::fun().

Если вместо этого вы просто вызовете метод fun в Derived::Derived (с использованием или без использования this->), вы в конечном итоге вызовете метод в Derived, см. Следующий пример:

#include <iostream>

class Base
{
    public:
    virtual void fun()
    {
      std::cout<<"In Base"<<std::endl;
    }
};

class Derived : public Base
{
    public:
    Derived(Base obj)
    {
      fun();
    }
    void fun()
    {
      std::cout<<"In Derived"<<std::endl;
    }
};

int main()
{
    Base b;
    Derived d(b);

    return 0;
}

Выход:

In Derived

Другой случай (который, возможно, имеет в виду), когда вы вызываете виртуальный метод в классе Base. Затем, когда создается переменная Derived, вызывается конструктор Base. Однако на данный момент Derived еще не создано, поэтому любой виртуальный вызов в конструкторе класса Base преобразуется в метод в Base. См. Обсуждение здесь , C ++ FAQ с веб-сайта Б. Страуструпа и FAQ на isocpp веб-сайте.

Когда наконец создается объект Derived, виртуальные вызовы работают как обычно.

Следующий пример иллюстрирует этот случай

#include <iostream>

class Base
{
    public:
    Base() { fun(); }
    virtual void fun()
    {
      std::cout<<"In Base"<<std::endl;
    }
    void callfun() { fun(); }
};

class Derived : public Base
{
    public:
    Derived(Base obj) { }
    void fun()
    {
      std::cout<<"In Derived"<<std::endl;
    }
};

int main()
{
    std::cout << "About to create Base: ";
    Base b;
    std::cout << "About to create Derived: ";
    Derived d(b);

    std::cout << "Calling b.callfun(): ";
    b.callfun();
    std::cout << "Calling d.callfun(): ";
    d.callfun();

    return 0;
}

Выход:

About to create Base: In Base
About to create Derived: In Base
Calling b.callfun(): In Base
Calling d.callfun(): In Derived
0 голосов
/ 16 ноября 2018

Поскольку вы передаете создателю фактический Base (не указатель или ссылку) то, что у вас есть, - фактический экземпляр Base - приведение указателя к Derived* недопустимо.

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