Virtual table and _vptr storage scheme

Can someone explain how this virtual table for another class is stored in memory? When we call a function using a pointer, how do they make a function call using an address? Can we get the size of a virtual table distribution in memory using a class pointer? I want to see how many memory blocks are used by a virtual table for a class. How can I see him?

class Base { public: FunctionPointer *__vptr; virtual void function1() {}; virtual void function2() {}; }; class D1: public Base { public: virtual void function1() {}; }; class D2: public Base { public: virtual void function2() {}; }; int main() { D1 d1; Base *dPtr = &d1; dPtr->function1(); } 

Thanks! in advance

+5
source share
5 answers

The first point to keep in mind is the disclaimer: none of this is guaranteed by the standard. The standard says what the code should look like and how it should work, but it doesnโ€™t really indicate exactly how it should happen by the compiler.

However, in fact, all C ++ compilers work in this respect in exactly the same way.

So, let's start with non-virtual functions. They come in two classes: static and non-static.

The easiest way is to use static member functions. A static member function is almost like a global function that is a friend class, except that it also needs a class name as a prefix to the function name.

Non-static member functions are a bit more complicated. They are still normal functions that are called directly, but they are passed by a hidden pointer to the instance of the object on which they were called. Inside the function, you can use the this to refer to the data of this instance. So, when you call something like a.func(b); , the generated code is very similar to the code you get for func(a, b);

Now consider virtual functions. Here, where we get the pointers in vtables and vtable. We have enough indirect attention that it is probably best to draw some diagrams to see how all this is laid out. Here's a pretty simple example: one instance of one class with two virtual functions:

enter image description here

Thus, the object contains its data and a pointer to the vtable. Vtable contains a pointer to each virtual function defined by this class. However, it may not be immediately apparent why we need to touch this. To understand this, let's look at the following (all so slightly) more complicated case: two instances of this class:

enter image description here

Note that each instance of the class has its own data, but both of them have the same vtable file and the same code. If we had more instances, they would still share one vtable among all instances of the same class.

Now consider derivation / inheritance. As an example, let's rename our existing class to "Base" and add a derived class. Because I feel creative, I will call him Derived. As above, the base class defines two virtual functions. A derived class overrides one (but not the other) of them:

enter image description here

Of course, we can combine these two, having several instances of each of the base and / or derived classes:

enter image description here

Now let's delve into this a bit more. Interestingly, we can pass a pointer / link to an object of a derived class to a function written to get a pointer / link to a base class, and it still works, but if you call a virtual function, you get a version for the actual class, not the base class . So how does it work? How can we treat an instance of a derived class as if it were an instance of a base class, and it still works? To do this, each derived object has a "subobject of the base class." For example, consider the following code:

 struct simple_base { int a; }; struct simple_derived : public simple_base { int b; }; 

In this case, when you instantiate simple_derived , you get an object containing two int s: a and b . a (part of the base class) is at the beginning of the object in memory, and b (the derived part of the class) follows this. Thus, if you pass the address of an object to a function waiting for an instance of the base class, it uses the parts (s) that exist in the base class, which the compiler places with the same offsets in the object, d be in the object of the base class, so the function can manipulate them without even knowing that she is dealing with an object of a derived class. Similarly, if you call a virtual function, all it needs to know is the location of the vtable pointer. How important this is, something like Base::func1 basically means that it follows the vtable pointer, and then uses a pointer to a function with some given offset from it (for example, a fourth function pointer).

At least now I will ignore multiple inheritance. This adds a rather complicated picture (especially when virtual inheritance is involved), and you did not mention it at all, so I doubt that you really care.

As for access to any of them or using in any way, besides just calling virtual functions, you can think of something for a specific compiler, but do not expect it to be portable at all. Although things like debuggers often need to look at things like this, the code used tends to be pretty fragile and compiler specific.

+5
source

The virtual table must be shared between instances of the class. More precisely, he lives at the level of "class", and not at the instance level. Each instance has overhead, actually having a pointer to a virtual table, if the hierarchy has virtual functions and classes in it.

The table itself at least has the size needed to hold the pointer for each virtual function. In addition, this is detailed information about the implementation, as it is actually determined. Check here for an SO question with more details on this.

+4
source

First of all, the following answer contains almost everything you want to know about virtual tables: fooobar.com/questions/224192 / ...

If you are looking for something more specific (with a regular disclaimer that may change between platforms, compilers and processor architectures):

  • If necessary, a virtual table is created for the class. The class will have only one instance of the virtual table, and each class object will have a pointer that points to the memory cell of this virtual table. The virtual table itself can be thought of as a simple array of pointers.
  • When you have assigned a derived pointer to a base pointer, it also contains a pointer to a virtual table. This means that the base pointer points to the virtual table of the derived class. The compiler will direct this call to the offset into the virtual table, which will contain the actual address of the function from the derived class.
  • Not really. Usually at the beginning of an object there is a pointer to a virtual table. But this will not help you too much, since it is just an array of pointers, without real indications of its size.
  • Short short answer: for the exact size, you can find this information in the executable file (or in segments loaded from it into memory). With enough knowledge of how a virtual table works, you can get a fairly accurate estimate, given that you know the code, the compiler, and the target architecture.

    For the exact size, you can find this information either in the executable file, or in segments in memory that are loaded from the executable file. An executable file is usually an ELF file; this kind of file contains the information necessary to run the program. Some of this information is a symbol for various types of language constructs, such as variables, functions, and virtual tables. For each character, it contains the size that is required in memory. Thus, the button string, you will need the symbol name of the virtual table and enough knowledge in ELF to extract what you want.
+3
source

Jerry Coffin's answer perfectly explains how virtual function pointers work to achieve polymorphism at run time in C ++. However, I believe he lacks an answer to where vtable is stored in memory. As others have noted, this is not dictated by the standard.

However, there is an excellent blog post (s) from Martin Kysel that details where virtual tables are stored. To summarize your blog (s):

  • One virtual table is created for each class (not an instance) with virtual functions. Each instance of this class points to the same virtual table in memory
  • Each vtable is stored in read-only memory of the resulting binary file.
  • The disassembly for each function in the vtable is stored in the text section of the received ELF binary
  • Attempting to write via vtable in read-only memory results in a segmentation error (as expected)
+1
source

Each class has a pointer to a list of functions, each of them in the same order for derived classes, then certain functions that are redefined are changed at this position in the list.

When you specify the type of the base pointer, the pointer object still has the correct _vptr.

Base

  Base::function1() Base::function2() 

D1's

  D1::function1() Base::function2() 

D2's

  Base::function1() D2::function2() 

Further derivatives of drom D1 or D2 will simply add their new virtual functions to the list below the current current.

When calling a virtual function, we simply call the corresponding index, function1 will be index 0

So your call

  dPtr->function1(); 

actually

  dPtr->_vptr[0](); 
-1
source

Source: https://habr.com/ru/post/1271242/


All Articles