1

我正在阅读Bruce Eckel的《用 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,对吧?那么“虚拟调用在运行时解决,因为对象不知道它属于成员函数所在的类,还是从它派生的某个类”是什么意思?

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

4

3 回答 3

7

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

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是错误的,因为动态类型是CC覆盖了函数。

A::add在您的示例中, as的重复B::add是不必要的 - 无论subadd对象的动态类型如何,两者都将以多态方式调用。

于 2010-03-02T13:41:22.680 回答
3

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

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

于 2010-03-02T13:31:21.013 回答
1

您的示例中的callsubadd();确实是 call this->subadd();。在A::add()中,的类型thisA*,但this可能指向一个C对象(假设 C 也从 A 派生)。编译A::add()时,编译器无法知道哪些虚函数被覆盖了class C。呼入 可能实际上this->subadd();呼入。因此,编译器别无选择,只能进行虚拟调用,当它知道指向哪里时在运行时解析。A::add()C::subadd()this->

于 2010-03-02T13:40:40.933 回答