【问题标题】:Calling virtual functions inside member functions在成员函数内部调用虚函数
【发布时间】:2010-03-02 13:27:54
【问题描述】:

我正在阅读 Bruce Eckel 的 Thinking in C++。在第 15 章(第 1 卷)的标题“构造函数中虚函数的行为”下,他说

如果你在里面会发生什么 构造函数,你调用一个虚拟的 功能?普通会员内部 功能你可以想象会发生什么 发生 - 虚拟呼叫已解决 在运行时,因为对象不能 知道它是否属于类 成员函数在,或者一些 派生自它的类。为了 一致性,你可能认为这是 里面应该发生什么 构造函数。

Bruce 在这里试图解释当您在对象的构造函数中调用虚函数时,不会表现出多态性,即只会调用当前类的函数,而不会是该函数的其他派生类版本。这是有效的,我可以理解,因为类的构造函数不会事先知道它是为它运行还是为其他衍生对象的创建运行。此外,如果这样做,它将在部分创建的对象上调用函数,这是灾难性的。

虽然我突然感到困惑是因为他在第一句话中陈述了普通成员函数,但他说虚拟调用将在运行时解析。但是等等,在一个类的任何成员函数中,当你调用另一个函数(无论是虚拟的还是非虚拟的)时,它自己的类版本只会被调用,对吧?例如

class A
{
    virtual void add() { subadd(); }
    virtual subadd() { std::cout << "A::subadd()\n"; }
};

class B : public A
{
    void add() { subadd(); }
    void subadd() { std::cout << "B::subadd()\n"; }
};

在上面的代码中,在A::add() 中,当调用subadd() 时,它总是调用A::subadd()B 也是如此,对吧?那么他所说的“虚拟调用在运行时被解析,因为对象不知道它是属于成员函数所在的类,还是从它派生的某个类”是什么意思?

他是在解释通过基类指针的调用吗? (我真的很怀疑)在这种情况下,他不应该写“在普通成员函数中”;据我目前的理解,任何从同一类的另一个成员函数中调用成员函数都不是多态的,如果弄错了,请纠正我。

【问题讨论】:

  • 现在这是“尝试一下”的好案例。
  • 我在发布之前尝试过,它只显示了各自的成员函数,就像我上面写的那样。
  • 如果你想让 B 从 A 派生,你没有告诉编译器。
  • @Eric:谢谢,修好了!

标签: c++ polymorphism virtual-functions member-functions


【解决方案1】:

你错了——进一步的派生类可能会覆盖一些虚函数,这意味着静态调用是错误的。因此,扩展您的示例:

class C : public B
{
public:
    // Not overriding B::add.
    void subadd() { std::cout << "C::subadd\n"; }
};

A *a = new C;
a->add();

这会动态调用B::add,而后者又会动态调用C::subadd。对B::subadd 的静态调用是错误的,因为动态类型是C,而C 会覆盖该函数。

在您的示例中,A::addB::add 的重复是不必要的 - 无论对象的动态类型如何,两者都会多态地调用 subadd

【讨论】:

  • 感谢展示这个例子,它让我明白了。 A::add() 在我尝试时调用了 C::subadd()。我现在明白了,对虚函数的任何调用(无论它驻留在任何地方)都不能是静态的,而应该是动态的。 +1,谢谢!
  • “我现在明白了,对虚函数的任何调用(无论它驻留在任何地方)都不能是静态的”这是不正确的。如果编译器知道确切的类型,则不需要进行虚拟调用。
  • 我同意,当它确定对象是 A 类型并且不需要多态性时,它会进行静态调用。例如。 A a; a.subadd();
  • @Eric:虽然在类函数之外是这样,但在成员函数内部,它不知道this 是否为Class* 是否为Caller*,因此总是进行后期绑定。
【解决方案2】:

当你调用另一个函数(无论是虚拟的还是非虚拟的)时,它自己的类版本只会被调用,对吧?

对不起,错了。构造函数是个例外。否则,您将处理一个完全构造的对象,该对象具有完全的多态性。在您后面的示例中(如果 B 继承自 A),称为 B::subadd() (只需将代码用于试驾:这确实是学习编程语言如何工作的最佳方式)。

【讨论】:

  • 啊:我没有阅读代码,但你的意图。您确定要 B 继承 A 吗?
  • 是的,是的,对不起,我没有在帖子中正确地写它,现在已修复。谢谢:)
【解决方案3】:

在您的示例中调用 subadd(); 实际上是调用 this-&gt;subadd();。在A::add() 中,this 的类型是A*,但this 可能指向C 对象(假设C 也派生自A)。在编译A::add() 时,编译器无法知道class C 中覆盖了哪些虚函数。 A::add() 中的调用 this-&gt;subadd(); 实际上可能调用 C::subadd()。因此,编译器别无选择,只能进行虚拟调用,当它知道this-&gt; 指向的位置时,它会在运行时解析。

【讨论】:

  • 我同意,但是A* 什么时候会指向 C 对象;只有在类之外,才会进行一些分配并调用(以实现多态性)。我问的是通常调用成员函数,即obj.member();,其中member() 调用submember(),它在层次结构中是虚拟的。
  • 感谢这里对this的解释,因为我有点困惑this总是Class*,但它可以是Caller*,就像你说的thisA* 用于 C 对象,如果从 A 指针调用。
猜你喜欢
  • 2012-12-13
  • 1970-01-01
  • 1970-01-01
  • 2013-06-29
  • 2014-01-04
  • 2015-12-30
  • 1970-01-01
  • 2018-02-08
  • 1970-01-01
相关资源
最近更新 更多