哪些功能是动态调度的?

时间:2011-07-05 17:03:33

标签: c++ inheritance polymorphism

我搜索过问题,查看论坛,书籍等。我可以识别方法的多态行为,并且在编译或运行时决定调用的方法时有很多简单的例子。但我对此代码感到困惑,其中C类继承自A:

的B继承
class A {
protected:
    int x;

public: 
    virtual void change() = 0;
    virtual void change(int a) { x = a; }
};

class B : public A {
public: 
    void change() { x = 1; }
};

class C : public B {
public: 
    void change() { x = 2; }
    void change(int a) { x = a*2; }
};

int main () { 
    B *objb = new B();
    C *objc = new C();
    A *obja;
    objb->change();
    obja = objc;
    objc->change();
    obja->change(5);
    // ...
}

许多示例告诉(并且很明显)发生了多态行为,并且在运行时中决定执行以下行时要调用的方法:

obja->change(5);

但我的问题是:

  1. 当我调用以下内容时会发生什么(从纯虚拟中覆盖)?

    objb->change();
    
  2. 当我呼叫以下内容时会发生什么(从虚拟但非纯粹的覆盖)?

    objc->change(5);
    
  3. 由于指针变量的类声明与对象相同,是否应在编译运行时确定方法调用?

3 个答案:

答案 0 :(得分:5)

如果编译器可以在编译时推断出实际类型,则可以避免虚函数调度。但它只能在证明行为等同于运行时调度时才能执行此操作。这是否发生取决于您的特定编译器的智能程度。

真正的问题是,你为什么关心?您显然理解调用虚函数的规则,并且行为的语义始终是运行时调度的那些,因此它对您编写代码的方式没有任何影响。

答案 1 :(得分:3)

有三个问题需要考虑。首先是重载决议: 在这种情况下,编译器使用表达式的静态类型 构造它所选择的一组函数。因此,如果你有 写成:

objb->change( 2 );

代码不会编译,因为没有change int范围内的B。如果没有change的话 在B的范围内,编译器会进一步查看并找到 change中的A(所有这些),但一旦找到名称,就会停止。

这是名称查找和函数重载解析,它完全是 静态的。

第二个问题是编译器应该调用哪个函数 已选择在界面中调用特定功能。如果选择了 函数是虚函数,调用的实际函数将是函数 在动态的最派生类中具有完全相同的签名 type,即所讨论的实际对象的类型。

最后,还有一个问题是动态调度是否被用于 生成的代码。这完全取决于编译器。该 编译器可以做任何想做的事,只要正确的函数,就像 由前两个问题决定,被称为。一般来说:如果 功能不是虚拟的,永远不会使用动态调度;如果 访问是直接对象(命名对象或临时),动态 通常不会使用dispatch,因为编译器可以很简单 知道最衍生的类型。当电话是通过参考或a 指针,编译器一般会使用动态调度,但它是 有时可能编译器跟踪指针足以知道 它将在运行时指向的类型,并放弃动态调度。和 好的编译器通常会使用分析器信息进一步发展 确定99%的时间,将调用相同的函数,并且 调用处于紧密循环中,并将生成两个版本的循环, 一个用动态调度,一个用最频繁调用 函数内联,并通过if,at选择循环的哪个版本 运行时。

答案 2 :(得分:1)

objb->change()

调用B::change(),因为objb包含B类型对象的地址

objc->change(5);

调用C::change(int),因为objc包含C类型对象的地址

方法调用仍然是 动态/运行时间 ,因为方法B::change()& C::change(int)仍然是虚拟的,因为继承了虚拟属性。

回答是否动态调度函数或编译时的问题

答案是否可以肯定地说它是编译时调度还是动态调度。动态/运行时调度首先发生,因为编译器无法确定在编译时调用哪些函数版本,因此如果编译器可以推断出要调用哪个函数的确定方式,则调度可能很好在编译时自己决定。

话虽如此,调度是在运行时还是编译时发生,并不会改变调用哪个版本的重载函数最终被调用的语义,因为C ++标准明确规定了这方面函数函数的规则。 / p>