C ++中虚拟函数的构造函数内部的动态绑定

时间:2018-11-16 15:06:31

标签: c++ constructor virtual-functions

根据标准,我们知道构造函数总是会在内部对其进行虚函数的早期绑定,因为它们对派生类层次结构的缺点尚不完全了解。

在这种情况下,如果在我的基本构造函数中使用了早期绑定,那么我已经将派生对象传递给了完全可以接受的基类指针(此处是向上转换)。如果使用早期绑定,则虚函数的选择应基于指针的类型(此处为Base *),而不是指针的内容(指针所指向的对象,因为我们不知道确切的对象)被指出)。在那种情况下,由于指针类型为Base *,在两种情况下我们都应该仅调用Base类虚拟函数。有人可以澄清一下吗?

我认为这里使用动态绑定,而不是早期绑定。如果我的理解错误,请纠正我。

调用base的输出的第一行是完全可以的

class Base
    {
        public:
        Base(){
            fun();
        }
        Base(Base *p)
        {
            p->fun();
        }
        virtual void fun()
        {
            cout<<"In Base"<<endl;
        }
    };

    class Derived : public Base
    {
        public:
        void fun()
        {
            cout<<"In Derived"<<endl;
        }
    };

    int main()
    {
        Derived d;
        Base b(&d);
    }

O / P:

In Base
In Derived

2 个答案:

答案 0 :(得分:1)

构造函数体内的虚拟调用规则适用于当前正在构造的对象,因为该对象尚未被视为任何派生类的对象。 (对于当前被破坏的对象也有类似的规则。)从使用编译时类型(例如语法ClassName::member_func() force)的意义上讲,它与“早期绑定”没有任何关系。

您的代码具有两个不同的对象db,并且d的构造函数在到达p->fun();行时已完全完成。

详细信息:

  1. 程序进入main
  2. 使用d的隐式声明的默认构造函数创建对象Derived
  3. Derived::Derived()要做的第一件事是通过调用默认构造函数Base::Base()创建基类子对象。
  4. Base::Base()的主体调用fun()。由于我们尚未输入Derived::Derived()构造函数的主体,因此此虚拟查找将调用Base::fun()
  5. Base::Base()完成。
  6. Derived::Derived()的正文为空,执行并结束。
  7. 回到main,通过将指向b的指针传递给构造函数d来创建对象Base::Base(Base*)
  8. Base::Base(Base *p)的主体调用p->fun()。由于p是指向d的指针,Derived已经是类型为Derived::fun()的完全构造的对象,因此此虚拟查找将调用Derived

与此示例稍有不同,我们定义了一个默认构造函数this,以将Base*(隐式转换为Base)传递给Base子对象的构造函数。 (这是有效的,但是如果以其他方式使用指针(例如在#include <iostream> using std::cout; using std::endl; class Base { public: Base(){ fun(); } Base(Base *p) { p->fun(); } virtual void fun() { cout<<"In Base"<<endl; } }; class Derived { public: Derived() : Base(this) {} virtual void fun() override { cout << "In Derived" << endl; } }; int main() { Derived d; } 的基或成员的初始化程序中使用指针可能会带来风险)。

Base::Base(Base *p)

该程序将仅打印“在基础中”,因为现在在p中,RAILS_MASTER_KEY确实指向当前正在构造的同一对象。

答案 1 :(得分:0)

原因是C ++类是从基类构造到派生类的,并且在对象创建过程完成时会创建完整对象的虚拟调用表。因此,在上面的代码摘录中调用了基类函数。除非另有规定,否则永远不要在构造函数中进行虚函数调用。以下是Bjarne Stroustrup's C++ Style and Technique FAQ的摘录:

  • 在构造函数中,虚拟调用机制被禁用,因为 派生类的替代尚未发生。对象是从头开始构造的,即“派生之前的基础”。

  • 销毁是在“基础类之前的派生类”完成的,因此是虚拟的 函数的行为与构造函数相同:仅使用本地定义– 并且不会调用覆盖函数来避免触摸(现在 销毁)对象的派生类部分。