Переопределение метода в c ++: это полиморфизм во время компиляции или во время выполнения? - PullRequest
0 голосов
/ 17 октября 2019

В C ++, Является ли переопределение функций полиморфизмом времени выполнения или полиморфизмом времени компиляции? Я думаю, что это полиморфизм времени компиляции, потому что каждая информация, которая требуется для вызова соответствующей функции, известна компилятору на ранних этапах. Как и в приведенном ниже коде, obj.disp(); Здесь компилятор знает, что obj является объектом базового класса, поэтому он немедленно разрешит вызов функции disp() базового класса. и то же самое для obj2.disp(); Здесь компилятор знает, что obj2 является объектом производного класса, поэтому он вызовет функцию disp () производного класса. Я не понимаю, почему это называется полиморфизмом во время выполнения.

Все еще в JAVA люди называют переопределение функций как полиморфизм во время выполнения. Пожалуйста, объясните кому-нибудь ....

#include <iostream>
using namespace std;

class A {
public:
  void disp(){
     cout<<"Super Class Function"<<endl;
  }
};

class B: public A{
public:
  void disp(){
     cout<<"Sub Class Function";
  }
};

int main() {
  //Parent class object
  A obj;
  obj.disp();
  //Child class object
  B obj2;
  obj2.disp();
  return 0;
}

Ответы [ 2 ]

0 голосов
/ 17 октября 2019

Является ли функция переопределением полиморфизма во время выполнения или полиморфизма во время компиляции

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

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

Пример (без рандомизации):

#include <iostream>

class Foo {
public:
    void not_virtual() { std::cout << "not_virtual\n"; }
    virtual void func() = 0;

    virtual ~Foo() = 0;
};

Foo::~Foo() {}

void Foo::func() {
    std::cout << "default implementation\n";
}

class FooImpl : public Foo {
public:
    void not_virtual() {      // hiding - Foo's not_virtual()
        std::cout << "not_virtual, but in FooImpl\n";
    }
    void not_virtual(int x) { // overloading - same name, different signature
        std::cout << "not_virtual " << x << "\n";
    }
    void func() override { std::cout << "Impl\n"; }
};

void interface_user(Foo& x) { // slice away all but the Foo part of the object
                              // but
    x.func();                 // func() is in the vtable, call child class' function
    x.not_virtual();          // not in the vtable, call Foo's function
    // x.not_virtual(2);      // error: Foo doesn't have this overload
}

int main() {
    FooImpl f;

    f.func();          // calling overridden func
    f.not_virtual();   // calling function hiding the base class function
    f.not_virtual(1);  // calling overloaded func

    interface_user(f); // slicing - the function takes a Foo&
}

Вывод:

Impl
not_virtual, but in FooImpl
not_virtual 1
Impl
not_virtual
0 голосов
/ 17 октября 2019

Во время выполнения разрешаются только вызовы виртуальных функций. Если функции не виртуальные, как в вашем примере, вызовы разрешаются во время компиляции.

При просмотре неоптимизированной сборки (сгенерированной gcc 9.2 с -O0), основная функция компилируется в:

main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        lea     rax, [rbp-1]
        mov     rdi, rax
        call    _ZN1A4dispEv
        lea     rax, [rbp-2]
        mov     rdi, rax
        call    _ZN1B4dispEv
        mov     eax, 0
        leave
        ret

Странные имена _ZN1A4dispEv и _ZN1B4dispEv являются результатом искажения имен: в подобных случаях компилятор создает уникальные имена для функций (имейте в виду, что в ассемблере нет опций). Фактическое искажение определяется компилятором, но вы можете заметить имя класса (A и B соответственно) и имя функции disp.

Если disp были объявлены какvirtual в A, тогда main будет выглядеть следующим образом:

main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
->      mov     eax, OFFSET FLAT:vtable for A+16
->      mov     QWORD PTR [rbp-8], rax
        lea     rax, [rbp-8]
        mov     rdi, rax
        call    A::disp()
->      mov     eax, OFFSET FLAT:vtable for B+16
->      mov     QWORD PTR [rbp-16], rax
        lea     rax, [rbp-16]
        mov     rdi, rax
        call    B::disp()
        mov     eax, 0
        leave
        ret

Строки, отмеченные ->, представляют собой запросы vtable, используемые для разрешения вызова функции во время выполнения.

...