1

考虑以下示例代码:

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 回答 4

4

在该行Base* bp = &d2中,对象类型在编译时是已知的。那么在这种情况下使用哪个函数bp->vf();也可以在编译时做出决定对吗?

是的。
这是编译器优化的一部分,大多数现代智能编译器都可以做到。

如果编译器可以自行决定在编译时调用哪个函数,那么它就会这样做。虽然这完全取决于编译器是否可以检测到在编译时或运行时调用的确切函数,但虚拟化可以保证您的程序所需的行为。

由于对象类型在编译时本身是已知的,那么这个示例程序中是否使用了虚函数的强大功能?

完全取决于编译器。大多数现代编译器将能够在编译时评估此函数调用。

于 2011-12-14T15:59:16.823 回答
3

这个程序很简单,并且确实没有很好地展示虚函数(或者更普遍的多态性)的强大功能。考虑这个变化:

// 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();

于 2011-12-14T16:06:20.553 回答
1

嗯...对这两个问题都是肯定的和否定的:这取决于您争论的抽象级别。

  • 从语言的角度来看,1) 是一种误解。for 的类型d2是已知的,但是当涉及到要分配的 d2 地址时,会发生bpfrom Derived*into的转换。Base*从 tham 开始,bp 的静态类型是Base*(因为那是它的声明)和它指向它的 Derived 的动态类型(因为这是与对象关联的运行时类型信息所指的)。从他们开始,通过 bp 的每个操作都假定 Base* 作为一种类型,并且需要对每个虚函数进行重定向。

  • 从编译器的角度来看,可以进行某些优化,并且由于在您的所有函数中,您的 pb 始终指向 Derived,因此可以跳过虚拟重定向。但这是由于您的特定示例的结构方式,而不是因为语言功能。

于 2011-12-14T16:12:53.670 回答
1

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 pointervptr指向一个称为虚拟表的表。每个类将有一个虚拟表,其中至少有一个虚拟函数(无论为该类创建了多少对象)。虚表包含类的虚函数的地址。

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。但是绘制的形状取决于传入的对象的实际类型。

于 2011-12-14T16:15:37.763 回答