【问题标题】:Virtual and non-virtual functions during compile-time and runtime (C++)编译时和运行时的虚拟和非虚拟函数 (C++)
【发布时间】:2015-09-06 22:40:52
【问题描述】:

我希望标题不会令人困惑。我试图理解以下由在 C++ 中定义类的虚拟或非虚拟方法引起的问题。想象一下,我有一个基类 A 和一个派生类 B,这样

class A { 
public:
    void print() { cout << "A"; } 
}

class B : A { 
public:
    void print() { cout << "B"; } 
}

如果我知道执行下面的代码,打印命令会打印出“A”。

A *a = new A();
B *b = new B();
((A *)b)->print(); // this prints "A"

但是,如果我将两个类中的“打印”方法声明为虚拟方法,我会在屏幕上看到打印的“B”。为什么会发生这种情况?

【问题讨论】:

  • 您能否澄清“为什么会发生这种情况?”这个问题?您是否要求在标准中提供指定语言的参考,或者您是否要求例如“什么是 vtable,它在典型实现中是如何工作的”
  • @ChrisBeck 我知道问题的答案是,如果方法不是虚拟的,那么调用的方法“取决于编译时使用的类型”,而如果方法是定义为虚拟的,那么“对象的运行时类型”确定调用哪个定义。那是我不完全明白的。
  • 会发生什么是旧样式转换用于访问私有基类;隐式转换不起作用,static_cast 不行,reinterpret_cast 可能在实践中起作用,但会给 UB。

标签: c++ runtime virtual-functions compile-time


【解决方案1】:

如果函数不是virtual,编译器将只使用表达式给出的任何类型。因此,当您将B 对象转换为A 对象时,它将调用A::print 函数。

如果你使用virtual,编译器会建立一个函数指针表[1],当函数被调用时,编译器会生成代码来通过该表调用,而不是仅仅查看当前type,它允许基类型调用派生类中的函数,从而允许多态行为。

[1] 从技术上讲,规范并没有告诉我们如何实现它,但几乎所有编译器都是这样工作的。如果编译器可以使用魔法产生相同的行为,则允许这样做 - 只要魔法可靠且可重现。

【讨论】:

    【解决方案2】:

    A *b = 新 B(); //LHS 是编译时 = RHS 是运行时(因为在运行时创建对象)
    B.print();

    如果打印是非虚拟的
    编译时间:A.print() 被解析为开始工作(因为它是一个真正的函数)
    运行时:A.print()已部署/派往工作。

    如果 print 是虚拟/非真实功能:
    编译时间:绕过 A.print() 并解析 B.print 开始工作 运行时:B.print() 已部署/调度到工作。

    【讨论】:

      猜你喜欢
      • 2022-01-11
      • 2020-07-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-12
      • 1970-01-01
      • 1970-01-01
      • 2016-05-20
      相关资源
      最近更新 更多