C ++ objects and virtual function table storage

C ++ objects and virtual function table storage

C ++ virtual functions mechanisms that implement multi-state, i.e. with a parent type pointer to the instance of its subclasses , and then calls the member function of the actual subclass through pointers parent class, this technique allows the pointer of the parent class has " a variety of forms, "this is a generic technology, which is using the same code to implement variable algorithm

This article describes the use of the method is no longer virtual functions, but to do a clear analysis of the implementation mechanism from virtual functions

Reference Hirofumi: Https://Blog.Csdn.Net/u012879957/article/details/81027287

For implementation mechanism, we must first understand the way objects are stored

And object data storage function

We know that with the class to define the object, the system will allocate for each object storage space

In your impression of the class of storage could be the next chart like this:

The upper panel shows the code assigned to each data storage function and the object, this is certainly not the memory utilization is too low, so that the C ++ compiler system uses the following method:

Each object data storage space occupied only part of the object (virtual function pointers and pointers belong to the virtual base class data portion), belonging to the common part of the function code

We often say that "A member function of an object," is from the logical point of view in terms of, and physical storage member functions in fact not the case

C ++ memory partition

C ++ memory partition is probably divided into five parts:

  1. Stack (stack): is automatically allocated by the compiler when required, you need not cleared automatically when the variable storage region, typically store local variables, function parameters.
  2. Heap (heap): is a newfree memory blocks allocated by the programmer (regardless of the compiler), typically a newone deletecorresponds to one new[]to one delete[]correspondence, if the programmer is not freed, the resources by the operating system automatically recovered at the end of the program
  3. Free store: is a mallocblock of memory allocated the like, and is very similar to the stack, with the freereleased
  4. Global / static memory: global variables and static variables are allocated to a same memory
  5. Constant storage area: This is a special storage area inside the store constants can not be modified

(Heap and the free store but it is in fact the same area, new underlying implementation code calls malloc, new advanced version of malloc can be seen as intelligent)

You may ask: static member functions and non-static member functions are placed in the class definition code memory area , so it can be said that they belong to the class, but the class Why can directly call the static class member function, rather than static class member function (even if the function has no parameters) can only call it class object

The reason: non-static class member function of a class actually contains a pointer to a pointer to an object class type parameters (that is, this pointer ), so only class object can be called (at this time this indicator has real value)

Vtable

C ++ to achieve polymorphism through inheritance and virtual functions, virtual functions through a virtual function table implementation, virtual function table to solve the succession, coverage, adding the issue of virtual functions, ensure that the actual function of the true reaction

Not too familiar friend, the following may look very ignorant, personal recommendations and down back and forth to see

Vtable principles outlined

C ++ virtual functions are implemented method: add an object class for each of a hidden member hides members hold a pointer that is called a virtual table pointer (the vptr), pointing to a virtual function table (virtual function table, vtbl)

Virtual function table like an array, table, there are many slots (slot) , each groove is the address of a virtual function (as will be appreciated storage array pointing to each virtual function pointer)

That is: for each class using a virtual function table, each class object with a virtual table pointer

In the instance of the object class has virtual functions, the table is allocated in the memory of the object instance (as said above it), when we use the pointer to operate a parent class when the child class, this table just as a map indicating the actual function should be called

Probably structure is as follows:

Above this figure, the virtual function table of the last added an extra node, which is the end node of the virtual function table, as the end of the string, in /0the same, which marks the end of the vtable, the end flag values under different compilers may be different

For example:
the base class virtual table object contains a pointer to the virtual function table base class
derived object table will also contain a dummy pointer to the derived class virtual function table

  • If the derived class overrides a virtual method in the base class, the derived class virtual function table stored address rewrite virtual function, rather than the address of the base class virtual function
  • If the base class virtual method is not overridden in a derived class, the derived class inherits the base class virtual method, and derived class virtual function table to save the address in the base class virtual function is not overridden, but If the new derived class defines a virtual method, the address of the virtual function will also be added to the derived class virtual function table

You may have dizzy, it does not matter, we use the following code examples show you

Find the virtual function table

C ++ compiler will ensure that pointer virtual function table is present in the object instance the foremost position (taken to ensure that the vtable has the highest performance, in the case where a multilayer multiple inheritance or inheritance), which means that we address this object instance to obtain the address of the virtual function table and can traverse wherein the function pointer and calls the appropriate function

We create a new class

class Base 
{
public:
    virtual void f() { cout << "Base::f" << endl; }
    virtual void g() { cout << "Base::g" << endl; }
    virtual void h() { cout << "Base::h" << endl; }
};

According to the above argument, we can get the virtual function table by way of example of the Base, the table (array) of the memory point F, the pointer g, h of these three functions

typedef void(*Fun)(void);

int main()
{
    Base bObj;
    Fun pFun = NULL;    
    //指向void* pf(void)类的函数的指针pFun

    cout << "虚函数表的地址:" << (int*)(&bObj) << endl;
    cout << "虚函数表的第一个函数地址:" << (int*) * (int*)(&bObj) << endl;
    //再次取址得到第一个虚函数的地址

    //第一个虚函数
    pFun = (Fun) * ((int*) * (int*)(&bObj));
    pFun();
}

We split open slowly to see this code

typedef void(*Fun)(void);

typedef void(*Fun)(void)Using type alias declare a function pointer address is NULL, equivalent totypedef decltype(void) *Fun

Several now insert a breakpoint, to observe the change pointer pFun:

1569492129695

Base objects are instantiated from the bObj, then Fun pFun=NULLit is declared to return a pointer to a function

Here breakpoints break in Fun pFun=NULLbefore, you can see pFun has not been initialized

After initialization pFun = NULL value becomes0x00000000

After that the object instance bobj, we (int*)(&bObj)forced to &bObjturn to int*obtain the address of the virtual function table, which is a pointer to the address of the virtual function table of the first element of the array, the pointer to fetch address again can get a first dummy address of the function (the first element of the array), which is the first virtual function Base::f()address

cout << "虚函数表的地址:" << (int*)(&bObj) << endl;
cout << "虚函数表的第一个函数地址:" << (int*) * (int*)(&bObj) << endl;
//再次取址得到第一个虚函数的地址

//第一个虚函数
pFun = (Fun) * ((int*) * (int*)(&bObj));
pFun();

You probably do not understand this operation, to (int*) * (int*)(&bObj)be understood, (int*)(&bObj)is to be cast objects bObj became int*the address, if a direct call *(int*)(&bObj)is pointing to data objects bObj address pointed to, but here is a virtual table, it refers to not in the past, must (int*)be converted into a function pointer to point, (int*) * (int*)(&bObj)point to objects bObj becomes a function of the address of the first

And because pFunby the Funfunction pointer declaration of this function, it is the equivalent of Fun entity, must then convert the address into a pFuntype of knowledge, that is coupled with (Fun)*mandatory conversion

The whole process is simple, it is read from the start address bObj four bytes of the content ( &bObj), then the contents interpreted as a memory address ( (int*)(&bObj)), and then access this address ( (int*) * (int*)(&bObj)), the last value stored in the address then interpreted as an address of a function ( (Fun) * ((int*) * (int*)(&bObj)))

PFun can see the value of virtual function tables have been equal to the first element ( _vfptr[0]) values 0x00b41168, and that is pFun this pointers to functions already pointing to the function f()(remember the virtual function table stored virtual function pointer is pointing to, the value is the addresses of the virtual functions)

Console output:

Like arrays, if you want to call Base::g()and Base::h()we can:

pFun = (Fun) * ((int*) * (int*)(&bObj));
    // (Fun) * ((int*) * (int*)(&bObj) + 1);    // Base::g()
    // (Fun) * ((int*) * (int*)(&bObj) + 2);    // Base::h()

Take a look at this picture, is not it a little more clearly?

Now let's look at the situation when they appear inheritance

Single inheritance (no cover)

class Base {
public:
    virtual void f() { cout << "Base::f()" << endl; }
    virtual void g() { cout << "Base::g()" << endl; }
    virtual void h() { cout << "Base::h()" << endl; }
};

class Derive :public Base {
public:
    virtual void f1() { cout << "Derive::f1()" << endl; }
    virtual void g1() { cout << "Derive::g1()" << endl; }
    virtual void h1() { cout << "Derive::h1()" << endl; }
};

typedef void(*Fun)(void);

int main()
{
    //Base bObj;
    Derive dObj;
    Fun pFun = NULL;
    cout << "虚函数表的地址:" << (int*)(&dObj) << endl;
    cout << "虚函数表的第一个函数地址:" << (int*) * (int*)(&dObj) << endl;
    pFun = (Fun) * ((int*) * (int*)(&dObj) + 0);
    pFun();
    pFun = (Fun) * ((int*) * (int*)(&dObj) + 1);
    pFun();
    pFun = (Fun) * ((int*) * (int*)(&dObj) + 2);
    pFun();
    pFun = (Fun) * ((int*) * (int*)(&dObj) + 3);
    pFun();
    pFun = (Fun) * ((int*) * (int*)(&dObj) + 4);
    pFun();
    pFun = (Fun) * ((int*) * (int*)(&dObj) + 5);
    pFun();
    return 0;
}

Break through vs, we found that to +3, the value pFun becomes a virtual address of a function f1:


operation result:

This does not cover inheritance, the subclass does not override any parent class, we instantiate an object DOB, its virtual function table as follows:

That

  1. Virtual functions declared in accordance with their order in the table placed
  2. Virtual function in the parent class virtual function subclass before

Single inheritance (covering)

Now we modify the class under Derive

class Base {
public:
    virtual void f() { cout << "Base::f()" << endl; }
    virtual void g() { cout << "Base::g()" << endl; }
    virtual void h() { cout << "Base::h()" << endl; }
};

class Derive :public Base {
public:
    virtual void f() { cout << "Derive::f()" << endl; }
    virtual void g1() { cout << "Derive::g1()" << endl; }
    virtual void h1() { cout << "Derive::h1()" << endl; }
};

The inheritance relationship, Derive is f()overloaded Base class f(), let's use the same method to debug, main function is essentially the same

int main()
{
    //Base bObj;
    Derive dObj;
    Fun pFun = NULL;
    cout << "虚函数表的地址:" << (int*)(&dObj) << endl;
    cout << "虚函数表的第一个函数地址:" << (int*) * (int*)(&dObj) << endl;
    pFun = (Fun) * ((int*) * (int*)(&dObj) + 0);
    pFun();
    pFun = (Fun) * ((int*) * (int*)(&dObj) + 1);
    pFun();
    pFun = (Fun) * ((int*) * (int*)(&dObj) + 2);
    pFun();
    pFun = (Fun) * ((int*) * (int*)(&dObj) + 3);
    pFun();
    pFun = (Fun) * ((int*) * (int*)(&dObj) + 4);
    pFun();
    pFun = (Fun) * ((int*) * (int*)(&dObj) + 5);
    pFun();
    return 0;
}

Can see into a first function Derive::f(), and to run pFun = (Fun) * ((int*) * (int*)(&dObj) + 5), the value becomes empty pFun

That structure is now virtual function tables like this:

That

  1. Covering the function f () is placed in the original position of the virtual table of the parent class virtual function
  2. Function is still not covered

Because of this feature, we can see this program, the following:

Base *b = new Derive();
b->f();

f b indicated by the memory in the virtual function table () position has been replaced Derive :: f () function addresses, so in the actual call occurs, is Derive :: f () is called, which C ++ enables dynamic polymorphism

Multiple inheritance (no cover)

class Base1 {
public:
    virtual void f() { cout << "Base1::f()" << endl; }
    virtual void g() { cout << "Base1::g()" << endl; }
    virtual void h() { cout << "Base1::h()" << endl; }
};

class Base2 {
public:
    virtual void f() { cout << "Base2::f()" << endl; }
    virtual void g() { cout << "Base2::g()" << endl; }
    virtual void h() { cout << "Base2::h()" << endl; }
};

class Base3 {
public:
    virtual void f() { cout << "Base3::f()" << endl; }
    virtual void g() { cout << "Base3::g()" << endl; }
    virtual void h() { cout << "Base3::h()" << endl; }
};

class Derive :public Base1, public Base2, public Base3 {
public:
    virtual void f1() { cout << "Derive::f1()" << endl; }
    virtual void g1() { cout << "Derive::g1()" << endl; }
};
typedef void(*Fun)(void);

int main()
{
    //Base bObj;
    Derive dObj;
    Fun pFun = NULL;
    cout << "虚函数表的地址:" << (int*)(&dObj) << endl;
    cout << "虚函数表的第一个函数地址:" << (int*) * (int*)(&dObj) << endl;
    pFun = (Fun) * ((int*) * (int*)(&dObj) + 0);
    pFun();
    pFun = (Fun) * ((int*) * (int*)(&dObj) + 1);
    pFun();
    pFun = (Fun) * ((int*) * (int*)(&dObj) + 2);
    pFun();
    pFun = (Fun) * ((int*) * (int*)(&dObj) + 3);
    pFun();
    pFun = (Fun) * ((int*) * (int*)(&dObj) + 4);
    pFun();
    pFun = (Fun) * ((int*) * (int*)(&dObj) + 5);
    pFun();
    return 0;
}

After breakpoints can see that when run here

pFun becomes null pointer

Console results

Why, after +5 not find it? Because in multiple inheritance, virtual function table storage point change took place, before we talk about C ++ compiler adds a hidden member within the object, now you can understand, he added more members to hide when multiple inheritance, but also means that we now have multiple virtual function table, the specific arrangement of FIG follows:

Then we have no way to access it? Powerful C ++ course is there, you should be careful discovered, the table (array) actually just turned into a two-dimensional array

int main()
{
    Fun pFun = NULL;
    Derive dObj;
    int** pVtab = (int**)& dObj;
    //Base1's vtable
    pFun = (Fun)pVtab[0][0];
    //等价于:pFun = (Fun) * ((int*) * (int*)((int*)& dObj + 0) + 0);
    pFun();
    pFun = (Fun)pVtab[0][1];
    pFun();
    pFun = (Fun)pVtab[0][2];
    pFun();

    //Derive's vtable
    pFun = (Fun)pVtab[0][3];
    pFun();
    //The tail of the vtable
    pFun = (Fun)pVtab[0][4];
    cout << pFun << endl;

    //Base2's vtable
    pFun = (Fun)pVtab[1][0];
    pFun();
    pFun = (Fun)pVtab[1][1];
    pFun();
    pFun = (Fun)pVtab[1][2];
    pFun();
    //The tail of the vtable
    pFun = (Fun)pVtab[1][3];
    cout << pFun << endl;

    //Base3's vtable
    pFun = (Fun)pVtab[2][0];
    pFun();
    pFun = (Fun)pVtab[2][1];
    pFun();
    pFun = (Fun)pVtab[2][2];
    pFun();
    pFun = (Fun)pVtab[2][3];
    cout << pFun << endl;
    return 0;
}

That

Multiple inheritance (covering)

class Base1 {
public:
    virtual void f() { cout << "Base1::f()" << endl; }
    virtual void g() { cout << "Base1::g()" << endl; }
    virtual void h() { cout << "Base1::h()" << endl; }
};

class Base2 {
public:
    virtual void f() { cout << "Base2::f()" << endl; }
    virtual void g() { cout << "Base2::g()" << endl; }
    virtual void h() { cout << "Base2::h()" << endl; }
};

class Base3 {
public:
    virtual void f() { cout << "Base3::f()" << endl; }
    virtual void g() { cout << "Base3::g()" << endl; }
    virtual void h() { cout << "Base3::h()" << endl; }
};

class Derive :public Base1, public Base2, public Base3 {
public:
    virtual void f() { cout << "Derive::f()" << endl; }
    virtual void g1() { cout << "Derive::g1()" << endl; }
};

main function will not go, eventually you will find the current virtual function table is as follows:

Security Issues

Water can carry a boat,Rowing alsoCan also capsize, let's take a look at the virtual table can be used to do something bad, right

Access via the parent pointer type subclass own virtual function

Although we can see Base1 virtual table has Derive virtual function in the above figure, but we simply can not use the following statement to call its own virtual function subclass:

Base1 *b1 = new Derive();
b1->f1();  //编译出错

Any attempt to use the parent class pointer to call the child class member functions of the parent class does not cover the behavior of the compiler will be considered illegal, therefore, such a program could not compile

But through multiple inheritance section of the code that you should have found

At runtime, we can access through the virtual function table pointer way to achieve a violation of C ++ semantics (that is, code that we use in multiple inheritance)

Fun pFun = NULL;
Derive dObj;
int** pVtab = (int**)& dObj;
//Base1's vtable
pFun = (Fun)pVtab[0][0];
//等价于:pFun = (Fun) * ((int*) * (int*)((int*)& dObj + 0) + 0);
//Derive's vtable
pFun = (Fun)pVtab[0][3];
pFun();
//The tail of the vtable
pFun = (Fun)pVtab[0][4];
cout << pFun << endl;

Access to non-public virtual functions

Parent non public virtual functions will also exist in the virtual function table , so we can use the same access virtual function tables to access the non-public virtual function, it is very easy to do

class Base {
private:
    virtual void f() { cout << "Base::f" << endl; }
};

class Derive : public Base {
};

typedef void(*Fun)(void);

void main() {
    Derive d;
    Fun  pFun = (Fun) * ((int*) * (int*)(&d) + 0);
    pFun(); //挖藕?
}

Finally, note

Virtual function tables are not necessarily exist at the beginning of most, but there are so many various compiler settings

Guess you like

Origin www.cnblogs.com/zhxmdefj/p/11594459.html