请考虑以下示例代码:
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()
问题:
在行Base* bp = &d2
中,对象类型在编译时是已知的。那么在bp->vf();
的情况下使用哪个函数的决定也可以在编译时做出来吗?
由于对象类型在编译时本身是已知的,因此该示例程序中使用的是虚函数的强大功能吗?
答案 0 :(得分:4)
在行
Base* bp = &d2
中,对象类型在编译时是已知的。那么在bp->vf();
的情况下使用哪个函数的决定也可以在编译时做出来吗?
是。
这是编译器优化的一部分,大多数现代智能编译器都能够做到这一点。
如果编译器可以确定在编译时调用哪个函数,那么它就会这样做。虽然这完全取决于编译器是否可以检测在编译时或运行时调用的确切函数,但虚拟主义可以保证您为程序所需的行为。
由于对象类型在编译时本身是已知的,因此该示例程序中使用的是虚函数的强大功能吗?
完全取决于编译器。大多数现代编译器都能够在编译时评估这个函数调用。
答案 1 :(得分: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();
。
答案 2 :(得分:1)
嗯......对于这两个问题都是肯定的和否定的:这取决于你所争论的抽象层次。
从语言的角度来看,1)是一种误解。 d2
的类型已知,但当d2地址分配给bp
时,将会发生从Derived*
到Base*
的转换。
从tham开始,bp的静态类型是Base*
(因为这是它的声明)和它指向其Derived的动态类型(因为这是与对象关联的运行时类型信息所引用的)。
从它们开始,通过bp的每个操作都假定Base *为一个类型,并且需要为每个虚函数重定向。
根据编译器的观点,可以进行某些优化,因为在您的所有函数中,您的pb始终指向Derived,实际上可以跳过虚拟重定向。 但这是由于您的特定示例的结构方式,而不是因为语言功能。
答案 3 :(得分: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 pointer
或vptr
的隐藏指针,指向名为虚拟表的表。每个类将有一个虚拟表,至少有一个虚函数(无论为该类创建了多少个对象)。虚拟表包含类的虚函数的地址。
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
派生的任何形状。但绘制的形状取决于传入的对象的实际类型。