首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >c++中动态调度的规则是什么?

c++中动态调度的规则是什么?
EN

Stack Overflow用户
提问于 2013-09-20 09:24:17
回答 4查看 495关注 0票数 10

我想知道动态调度在C++中到底是如何工作的。为了说明我的问题,我将从一些Java代码开始。

代码语言:javascript
运行
复制
class A
{
  public void op(int x, double y) { System.out.println("a"); }
  public void op(double x, double y) { System.out.println("b"); }
}

class B extends A
{
  public void op(int x, double y) { System.out.println("c"); }
  public void op(int x, int y) { System.out.println("d"); }
}

class C extends B
{
  public void op(int x, int y) { System.out.println("e"); }
}

public class Pol
{
  public static void main(String[] args)
  {
    A a = new C();
    B b = new C();

    /* 1 */ a.op(2, 4);
    /* 2 */ b.op(2.0, 4.0);
  }
}

调用a.op(2, 4)将打印"c",因为实际上编译器:

  • 查看类A (因为a被声明为A类型的变量),哪个方法最接近op(int, int)
  • 无法找到op(int, int)方法,但找到方法op(int, double) (使用单个自动转换int -> double),
  • 然后修复这个签名。

在执行过程中,JVM:

  • 查找编译器将签名op(int, double)修复到类C中的方法,但没有找到,
  • 查看C的超类,即B
  • 最后找到一个方法op(int, double),然后调用它。

同样的原则也适用于调用b.op(2.0, 4.0),它打印"b“。

现在,考虑一下C++中的等效代码

代码语言:javascript
运行
复制
#include <iostream>

class A
{
public:
  virtual void op(int x, double y) { std::cout << "a" << std::endl; }
  virtual void op(double x, double y) { std::cout << "b" << std::endl; }
};

class B : public A
{
public:
  void op(int x, double y) { std::cout << "c" << std::endl; }
  virtual void op(int x, int y) { std::cout << "d" << std::endl; }
};

class C : public B
{
public:
  void op(int x, int y) { std::cout << "e" << std::endl; }
};

int main()
{
  A *a = new C;
  B *b = new C;

  /* 1 */ a->op(2,  4);
  /* 2 */ b->op(2.0, 4.0);

  delete a;
  delete b;
}

a->op(2, 4)会像Java一样打印"c“。但是b->op(2.0, 4.0)再次输出"c“,在那里,我迷路了。

在C++中编译和执行动态调度时究竟应用了哪些规则?(注意,如果在每个函数前面编写virtual,您将从virtual代码中得到相同的行为;这里没有任何变化)

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2013-09-20 09:32:35

对于C++,当您执行b->op(2.0, 4.0);时,编译器在B中查找一个方法,它可以调用(int x, double y)并使用它。如果子类中有任何方法能够处理调用,则不查看超类。这叫做方法隐藏。op(double, double)是隐藏的。

如果要使其选择(double x, double y)版本,则需要在B中使用以下声明在B中使该函数可见

代码语言:javascript
运行
复制
using A::op;

进一步解释“规则”

票数 3
EN

Stack Overflow用户

发布于 2013-09-20 09:34:05

通过在op中声明一个新的重载到B,您已经隐藏了基本版本。编译器将只根据'B‘进行分派,这就是它选择op(int,double)的原因。

票数 1
EN

Stack Overflow用户

发布于 2013-09-20 10:22:16

如果您告诉编译器,编译器会在转换时发出警告/错误。使用gcc,编译器参数-Wconversion -Werror将阻止您的代码编译,因为您是对的,在这里有一个潜在的精度损失。

考虑到您没有打开此编译器选项,编译器很乐意将对b->op(double,double)的调用解析为B::op(int,double)。

请记住,这是一个编译时决策--而不是运行时/多态决策。

"b“指针的实际vtable在运行时将有一个方法op(int,int)可用,但是编译器在编译时不知道这个方法。它只能假定b指针的类型为B*。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18913202

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档