C ++中的方法覆盖:是编译时还是运行时多态性?



在C++中,函数覆盖运行时多态性还是编译时多态性?我认为这是编译时多态性,因为编译器很早就知道调用适当函数所需的每个信息。如下面的代码所示,obj.disp();这里的编译器知道 obj 是基类的对象obj2.disp();,因此它将立即解决对基类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;
}

运行时仅解析虚拟函数调用。如果函数是非虚拟的(如示例中所示(,则在编译时解析调用。

查看未优化的程序集(由带有 -O0 的 gcc 9.2 生成(,main 函数被编译为:

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是名称重整的结果:在这种情况下,编译器为函数创建唯一的名称(请记住,在汇编中没有oop(。实际的重整是编译器定义的,但你可以注意到类的名称(分别为AB(和函数的名称disp

如果dispA中声明为virtual,则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 查询,用于在运行时解析函数调用。

函数是覆盖运行时多态性还是编译时多态性

您只能override虚拟方法,这些方法支持运行时多态性。如果所有事实在编译时都已知,那么聪明的编译器可能会在没有vtables的情况下进行管理(对于虚拟调度(,但如果不是,则必须在运行时解决。

让随机数生成器从不同的子类创建对象,并将它们传递给接受基类指针(或引用(的函数,该函数仍然能够调用对象的重写函数。此问题在运行时解决。隐藏基类函数或子类重载的函数将不可用。

一个例子(没有随机化(:

#include <iostream>
class Foo {
public:
void not_virtual() { std::cout << "not_virtualn"; }
virtual void func() = 0;
virtual ~Foo() = 0;
};
Foo::~Foo() {}
void Foo::func() {
std::cout << "default implementationn";
}
class FooImpl : public Foo {
public:
void not_virtual() {      // hiding - Foo's not_virtual()
std::cout << "not_virtual, but in FooImpln";
}
void not_virtual(int x) { // overloading - same name, different signature
std::cout << "not_virtual " << x << "n";
}
void func() override { std::cout << "Impln"; }
};
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

最新更新