重写非虚拟方法

Overriding Non-Virtual methods?

本文关键字:方法 虚拟 重写      更新时间:2023-10-16

下面是一些代码,旨在显示何时覆盖虚拟方法。它输出:BB一个一个B一个这是对的吗?我以为酒吧方法不能被覆盖...?

#include <iostream>
using namespace std;

class A
{
public:
    virtual void foo(){cout<<"A"<<endl;}
            void bar(){cout<<"A"<<endl;}
};
class B : public A
{
public:
    void foo(){cout<<"B"<<endl;}
    void bar(){cout<<"B"<<endl;}
};
int main()
{
    B b;
    A * pA = new A;
    A * pA2 = &b;
    b.foo(); b.bar();
    pA->foo(); pA->bar();
    pA2->foo(); pA2->bar();
}    

我没有看到任何错误:

B b;
b.foo(); b.bar();

声明并初始化B的实例,编译类型为B,运行时类型为B。不需要多态性(也无法实现,因为不涉及指针(。

A * pA = new A;
pA->foo(); pA->bar();

声明指向A的指针,并使用 A 的实例对其进行初始化。即使在这种情况下不需要多态性也有效,因此调用Afoo()bar()

A * pA2 = &b;
pA2->foo(); pA2->bar();

声明指向A的指针,并使用对 B 的引用对其进行初始化。编译时类型为A*,运行时类型为B*。通过虚函数应用多态性,因此B::foo()虚方法,而非虚拟方法A::bar()

b.foo()给出B

b.bar()还给出了隐藏A::bar()B。它不是覆盖,而是名称隐藏。

pA->foo()给出ApA->bar()给出A

pA2->foo()给出了B,因为它是一个虚拟函数。

pA2->bar()给出了A,因为它不是虚拟函数。它是静态链接的,它调用A::bar()

前两个输出都应该是 B,因为您要在 B 类型的对象上调用 foo(( 和 bar((。由于你没有使用指针,编译器知道它是 B 类型的对象,因此不需要查阅 vtable 来调用该方法,所以即使 bar(( 不是虚拟的,编译器也知道使用 B 实现。

接下来的两个输出都应该是 A,因为您要在指向 A 类型的对象的指针上调用 foo(( 和 bar((。在这种情况下,foo(( 是虚拟的,vtable 指向该方法的 A 实现。bar(( 不是虚拟的,因此编译器决定使用 A 实现。

最后两个输出应该是 B 和 A,因为您要在指向 B 类型的对象的指针上调用 foo(( 和 bar((。在这种情况下,foo(( 是虚拟的,vtable 指向该方法的 B 实现。bar(( 不是虚拟的,因此编译器决定使用 A 实现,因为您使用的是指向类型 A 对象的指针。

这是正确的,pA2->bar(( 调用没有被覆盖。 即使它是一个 B 对象,你也会将其强制转换为 A,编译器会为 A 类调用 A 成员函数。

foo(( 是虚拟的,因此包含在类 B 中的是一个指针,指向哪个版本的 foo(( 对于该类是正确的,无论它被强制转换为何处。

此包含称为指向虚函数表的指针。 一旦你的类有虚拟成员,就会为它创建一个静态函数表(并且还将包括任何其他后续虚函数(,此外还有一个常量隐藏成员指针指向此表。 当您强制转换对象时,函数表保持不变,任何虚拟函数都将保持"附加"到原始类型。

PS,别忘了删除pA...您当前有内存泄漏:)