Works C ++ virtual functions

Static binding and dynamic binding

Discuss static binding and dynamic binding, you first need to understand is binding, what is binding? Associated with the function call the function itself, and the relationship between members access to the variable memory addresses, called binding. We understand and then be understood static and dynamic binding.

  • Static binding: refers to the process of compiling the program, the function call in response to the calling procedure with the desired combination of code, static binding. It occurs at compile time.
  • Dynamic binding: determining means during execution of the actual type of the referenced object, which calls the appropriate method for the actual type. During program execution, the procedure code in response to the function call and call the desired combination is called dynamic binding. It occurred in the run.

Dynamic binding in C ++

C ++ dynamic binding is achieved via virtual functions, particularly in the form of a multi-state implementation. The virtual function is achieved by the virtual function table. The table records the address of the virtual function, resolve inheritance issues covered by the guarantee can call the correct function of the actual type of the object dynamic binding. This virtual function tables in place? Standard C ++ specification comes in, the compiler must ensure vtable pointer is present in the object instance in the foremost position (which is taken to ensure correct offset to the virtual function) . That is, we can obtain this address via the virtual function table of the object instance, which then can traverse the function pointer and calls the corresponding function.

It works virtual functions

To understand dynamic binding, it is necessary to understand the working principle of virtual functions. Implemented in C ++ virtual functions are typically implemented by a virtual function table (C ++ specification does not specify which particular method used, but most of the compiler vendors have chosen this method). Class virtual function table is one continuous memory, a JMP instruction address recorded in each memory cell. The compiler creates a virtual function table for each class has virtual functions, the virtual function table to be shared by all objects of that class. Each member class virtual function table occupied virtual line. If there are N class virtual function, then the virtual function table will have N * 4 bytes in size.

Virtual function (virtual) is achieved by means of virtual function tables in this table, the main table is the address of the virtual function of a class , this table to solve the succession, covering the problems and ensure a true reflection of its actual function. Thus, in the instance of a class with a virtual function pointer to the allocated memory table (located foremost object instance), so that, when the parent class pointer to a subclass of operation when this virtual function table it is important to indicate the actual function to be called. How does it indicate it? It will be mentioned later.

JMP instruction is an unconditional branch instruction assembler language, unconditional jump instruction to go to any section of the memory. Transfer may be given in the instruction address may be given in a register, or indicated in the reservoir.

First we define the base class with a virtual function

class Base
{
public:
    virtual void fun1(){
        cout<<"base fun1!\n";
    }
    virtual void fun2(){
        cout<<"base fun2!\n";
    }
    virtual void fun3(){
        cout<<"base fun3!\n";
    }

    int a;
};

View the memory layout
Write pictures described here
we can see on the memory layout of the Base class, storage virtual function table pointer to the first position, followed by those member variables of Base. Further, there is a virtual function table, the table storage of all virtual functions Base class.

Since the virtual function table pointer is usually placed in front of the position of the object instance, then we should be able to access the virtual function table through code, a better understanding of the virtual table through the following code:

#include "stdafx.h"
#include<iostream>
using namespace std;

class Base
{
public:
    virtual void fun1(){
        cout<<"base fun1!\n";
    }
    virtual void fun2(){
        cout<<"base fun2!\n";
    }
    virtual void fun3(){
        cout<<"base fun3!\n";
    }

    int a;
};

int _tmain(int argc, _TCHAR* argv[])
{
    typedef void(*pFunc)(void);
    Base b;
    cout<<"虚函数表指针地址:"<<(int*)(&b)<<endl;

    //对象最前面是指向虚函数表的指针,虚函数表中存放的是虚函数的地址
    pFunc pfun;
    pfun=(pFunc)*((int*)(*(int*)(&b)));  //这里存放的都是地址,所以才一层又一层的指针
    pfun();
    pfun=(pFunc)*((int*)(*(int*)(&b))+1);
    pfun();
    pfun=(pFunc)*((int*)(*(int*)(&b))+2);
    pfun();

    system("pause");
    return 0;
}

operation result:
Write pictures described here

By this example, a virtual function table pointer, the virtual function table has enough understanding. Here again deeper. C ++ is how to use the base class pointers and virtual functions to achieve polymorphism of it? Here, we need to figure out how to work in a virtual function tables are inherited environment. Currently only understand single inheritance, as virtual inheritance, multiple inheritance to be understood later.
Single inheritance code is as follows:

class Base
{
public:
    virtual void fun1(){
        cout<<"base fun1!\n";
    }
    virtual void fun2(){
        cout<<"base fun2!\n";
    }
    virtual void fun3(){
        cout<<"base fun3!\n";
    }

    int a;
};

class Child:public Base
{
public:
    void fun1(){
        cout<<"Child fun1\n";
    }
    void fun2(){
        cout<<"Child fun2\n";
    }
    virtual void fun4(){
        cout<<"Child fun4\n";
    }
};

Memory layout comparison:
Write pictures described here
Write pictures described here
By contrast, we can see:

  • In single inheritance, class overrides Base Child class virtual function of the same name, embodied as a corresponding position is replaced by a new function Child class virtual function table, and the function is not covered not changed.
  • For subclasses own virtual function, directly appended to the virtual function table.

Further, we note that the Base and class Child classes have only a pointer vfptr, we said earlier, the pointer to the virtual function table, and we output the Child classes of the class vfptr Base:

int _tmain(int argc, _TCHAR* argv[])
{
    typedef void(*pFunc)(void);
    Base b;
    Child c;
    cout<<"Base类的虚函数表指针地址:"<<(int*)(&b)<<endl;
    cout<<"Child类的虚函数表指针地址:"<<(int*)(&c)<<endl;

    system("pause");
    return 0;
}

operation result:
Write pictures described here

可以看到,类Child和类Base分别拥有自己的虚函数表指针vfptr和虚函数表vftable。

下面这段代码,说明了父类和基类拥有不同的虚函数表,同一个类拥有相同的虚函数表,同一个类的不同对象的地址(存放虚函数表指针的地址)不同。

int _tmain(int argc, _TCHAR* argv[])
{
    Base b;
    Child c1,c2;
    cout<<"Base类的虚函数表的地址:"<<(int*)(*(int*)(&b))<<endl;
    cout<<"Child类c1的虚函数表的地址:"<<(int*)(*(int*)(&c1))<<endl;  //虚函数表指针指向的地址值
    cout<<"Child类c2的虚函数表的地址:"<<(int*)(*(int*)(&c2))<<endl;

    system("pause");
    return 0;
}

运行结果:
Write pictures described here

在定义该派生类对象时,先调用其基类的构造函数,然后再初始化vfptr,最后再调用派生类的构造函数( 从二进制的视野来看,所谓基类子类是一个大结构体,其中this指针开头的四个字节存放虚函数表头指针。执行子类的构造函数的时候,首先调用基类构造函数,this指针作为参数,在基类构造函数中填入基类的vfptr,然后回到子类的构造函数,填入子类的vfptr,覆盖基类填入的vfptr。如此以来完成vfptr的初始化)。也就是说,vfptr指向vftable发生在构造函数期间完成的。

动态绑定例子:

#include "stdafx.h"
#include<iostream>
using namespace std;

class Base
{
public:
    virtual void fun1(){
        cout<<"base fun1!\n";
    }
    virtual void fun2(){
        cout<<"base fun2!\n";
    }
    virtual void fun3(){
        cout<<"base fun3!\n";
    }

    int a;
};

class Child:public Base
{
public:
    void fun1(){
        cout<<"Child fun1\n";
    }
    void fun2(){
        cout<<"Child fun2\n";
    }
    virtual void fun4(){
        cout<<"Child fun4\n";
    }
};


int _tmain(int argc, _TCHAR* argv[])
{
    Base* p=new Child;
    p->fun1();
    p->fun2();
    p->fun3();

    system("pause");
    return 0;
}

运行结果:
Write pictures described here
结合上面的内存布局:
Write pictures described here

其实,在new Child时构造了一个子类的对象,子类对象按上面所讲,在构造函数期间完成虚函数表指针vfptr指向Child类的虚函数表,将这个对象的地址赋值给了Base类型的指针p,当调用p->fun1()时,发现是虚函数,调用虚函数指针查找虚函数表中对应虚函数的地址,这里就是&Child::fun1。调用p->fun2()情况相同。调用p->fun3()时,子类并没有重写父类虚函数,但依旧通过调用虚函数指针查找虚函数表,发现对应函数地址是&Base::fun3。所以上面的运行结果如上图所示。

Here, you have to understand why the point subclass pointer instance of the base class can be called a subclass of (virtual) function? Each instance of an object exists vfptr pointer, the compiler will first remove vfptr value, this value is the address of the virtual function table vftable, then the called function vftable according to this value. Therefore, as long as vfptr different vtable vftable point is different, and different virtual function table stores the address of the corresponding class virtual function, thus achieving a polymorphism "effect."

Focus on micro-channel public number, push the back-end development, block chain technology to share!

Guess you like

Origin www.cnblogs.com/s-lisheng/p/11287214.html