简体   繁体   English

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

[英]Dynamic binding inside constructor for virtual Function in C++

As per the standard, we know that constructor always go for an early binding of a virtual function inside them because they don't have complete idea the derived class hierarchy downside. 按照标准,我们知道构造函数总是在内部早先绑定虚拟函数,因为它们对派生类层次结构的缺点尚不完全了解。

In this case if early binding is used inside my base constructor, I have passed a derived object to a base class pointer which is completely acceptable (an upcasting is done here). 在这种情况下,如果在我的基本构造函数中使用了早期绑定,那么我已经将派生对象传递给了完全可以接受的基类指针(此处是向上转换)。 If early binding is used the selection of the virtual function should be based on the type of the pointer (which is Base * here) but not the content of the pointer(the object pointed by the pointer because we don't know the exact object being pointed). 如果使用早期绑定,则虚函数的选择应基于指针的类型(此处为Base *),而不是指针的内容(指针所指向的对象,因为我们不知道确切的对象)被指出)。 In that case since the pointer type is Base * we should have invoked only Base class virtual function in both cases. 在那种情况下,由于指针类型为Base *,在这两种情况下,我们只应调用Base类虚函数。 Could some one please clarify this? 有人可以澄清一下吗?

I think dynamic binding is used here rather than early binding. 我认为这里使用动态绑定,而不是早期绑定。 Please correct me if my understanding is wrong. 如果我的理解是错误的,请纠正我。

The first line of the output which invokes base is completely fine 调用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 : O / P:

In Base
In Derived

The rule for virtual calls within a constructor body applies to an object currently being constructed , because that object is not yet considered to be an object of any derived class. 构造函数体内的虚拟调用规则适用于当前正在构造的对象,因为该对象尚未被视为任何派生类的对象。 (And there's a similar rule for an object currently being destroyed.) It has little to do with "early binding" in the sense of using a compile-time type, such as the syntax ClassName::member_func() forces. (对于当前被销毁的对象,也有类似的规则。)从使用编译时类型(例如语法ClassName::member_func()的意义上讲,它与“早期绑定”没有任何关系。

Your code has two different objects d and b , and the constructor of d has entirely finished by the time you get to the p->fun(); 您的代码具有两个不同的对象db ,并且在到达p->fun();时, d的构造函数已完全完成p->fun(); line. 线。

In detail: 详细:

  1. The program enters main . 程序进入main
  2. The object d is created using the implicitly declared default constructor of Derived . 使用隐式声明的默认构造函数Derived创建对象d
  3. The first thing Derived::Derived() does is create the base class subobject, by calling the default constructor Base::Base() . Derived::Derived()所做的第一件事是通过调用默认的构造函数Base::Base()来创建基类子对象。
  4. The body of Base::Base() calls fun() . Base::Base()的主体调用fun() Since we have not yet entered the body of the Derived::Derived() constructor, this virtual lookup invokes Base::fun() . 由于我们尚未进入Derived::Derived()构造函数的主体,因此此虚拟查找将调用Base::fun()
  5. Base::Base() finishes. Base::Base()完成。
  6. The body of Derived::Derived() , which is empty, executes and finishes. 空的Derived::Derived()主体执行并完成。
  7. Back in main , the object b is created by passing a pointer to d to the constructor Base::Base(Base*) . 回到main ,通过将指向d的指针传递给构造函数Base::Base(Base*)来创建对象b
  8. The body of Base::Base(Base *p) calls p->fun() . Base::Base(Base *p)的主体调用p->fun() Since p is a pointer to d , which is already a completely constructed object of type Derived , this virtual lookup invokes Derived::fun() . 由于p是指向d的指针,该d已经是一个完全构造的Derived类型的对象,因此此虚拟查找将调用Derived::fun()

Contrast with this slightly different example, where we define a default constructor of Derived to pass this (implicitly converted to Base* ) to the constructor for the Base subobject. 与这个稍有不同的示例相反,我们定义了默认的Derived构造函数,以将this (隐式转换为Base* )传递给Base子对象的构造函数。 (This is valid, though could be risky if the pointer were used in other ways, such as in an initializer for a base or member of 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;
}

This program will print just "In Base", since now in Base::Base(Base *p) , p does point at the same object which is currently being constructed. 此程序仅在“ Base”中打印,因为现在在Base::Base(Base *p)p确实指向当前正在构造的同一对象。

The reason is that C++ classes are constructed from Base classes to derived classes and virtual call table of the complete object is created when the object creation process is completed. 原因是C ++类是从基类构造到派生类的,并且在对象创建过程完成时会创建完整对象的虚拟调用表。 Therefore, the base class function is called in the code excerpt above. 因此,在上面的代码摘录中调用了基类函数。 Unless otherwise mandatory, you should never make virtual function calls in the constructor. 除非另有规定,否则永远不要在构造函数中进行虚函数调用。 Below is an excerpt from Bjarne Stroustrup's C++ Style and Technique FAQ : 以下是Bjarne Stroustrup的C ++样式和技术常见问题解答的摘录:

  • In a constructor, the virtual call mechanism is disabled because overriding from derived classes hasn't yet happened. 在构造函数中,虚拟调用机制被禁用,因为尚未发生从派生类的重写。 Objects are constructed from the base up, “base before derived”. 对象是从头开始构建的,即“派生之前的基础”。

  • Destruction is done “derived class before base class”, so virtual functions behave as in constructors: Only the local definitions are used – and no calls are made to overriding functions to avoid touching the (now destroyed) derived class part of the object. 销毁是在“派生类先于基类”完成的,因此虚拟函数的行为与构造函数相同:仅使用局部定义-不会调用覆盖函数,以避免触及对象的(现已销毁)派生类部分。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM