1

我知道当使用指向派生类对象的基类指针调用虚函数时,编译器会使用动态绑定来调用派生版本。但是当使用指向基类对象的基类指针调用虚函数时,编译器是使用动态绑定还是静态绑定来调用虚函数呢?

例如:

class Base
{
public:
    virtual void show()
    {
        cout << "base class";
    }
}
int main()
{
    Base *pb; //Base class pointer
    Base b;   //Base class object
    pb = &b;
    pb->show(); //Is static binding or dynamic binding?
}

因为我的英语很差,所以我想让我的问题尽可能简单,但我会在下面更详细地描述我的问题:

其实问题源于我在总结如何触发动态绑定。首先我总结一下触发条件是:

  1. 该函数必须是一个虚函数。
  2. 必须使用指针或引用来调用函数。

这两个触发条件导致了我问的问题:“当基类指针指向基类对象时,编译器是否会使用动态绑定?”

我有谷歌搜索答案,我找到了一个片段(演示在这里):

struct A {
   virtual void f() { cout << "Class A" << endl; }
};

struct B: A {
   //Note that it not overrides A::f.
   void f(int) { cout << "Class B" << endl; }
};

struct C: B {
   void f() { cout << "Class C" << endl; }
};

int main() {
   B b; C c;
   A* pa1 = &b;
   A* pa2 = &c;
//   b.f();
   pa1->f(); 
   pa2->f();
}

以下是上述示例的输出:

"Class A"
"Class C"

根据pa1->f()遗嘱输出Class A,我总结了第三个触发条件:

3.基类中的函数必须在派生类中被覆盖。

现在根据三个触发条件,当使用指向基类对象的基类指针调用虚函数时,编译器会使用静态绑定来调用虚函数,因为虚函数没有被覆盖。

但是当使用指向派生类对象的派生类指针调用虚函数时,会使用动态绑定,因为虚函数被覆盖了。

这让我很困惑。

4

3 回答 3

3

它可以选择任何一个,也可以不选择,这取决于它的智能程度和检测能力。规则是多态必须起作用。这是如何实现的,是一个实现细节。

如果使用动态或静态绑定都可以实现相同的最终结果,就像这里的情况一样,那么两者都是编译器的有效选项。

在您的情况下,根本不必调用该函数 - 生成的代码可能与生成的代码完全相同

int main()
{
    cout << "base class";
}
于 2013-08-19T13:25:47.207 回答
2

简短的回答:不。至少在理论上不是。因为理论上,编译器不知道指针是指向 a Base、 aDerived还是指向YetAnotherDerived对象。因此,无论对象的动态类型如何,它都必须应用相同的机制。

但是:实际上,编译器有优化器,能够识别一些动态类型已知的用例。在您的情况下,它可以检测到别名,这意味着它知道pb指向b并且它是局部变量并且不能同时更改,因此它知道实际上您正在调用b.show()并将缩写输出以反映该事实并摆脱虚拟调度。类似的优化是可能的,例如在这段代码中:

auto pb = make_unique<Base>();
pb->show();

但是作为任何优化,是否应用它们取决于编译器 - 标准说即使指针指向 Base 对象也会发生虚拟调度,仅此而已。

于 2013-08-19T13:30:53.007 回答
2

我想这取决于编译器优化。编译器可能足够聪明,可以找出Base::show总是被调用的那个,也可能不是。你可以看看拆解来找出答案。您可以强制使用静态绑定b->Base::show()

于 2013-08-19T13:26:23.440 回答