2

As explained in one of the solutions of this question (Size of virtual pointer-C++) that you can calculate virtual pointer size in following manner :

struct virtual_base {
    int data;
    virtual_base() {}
    virtual ~virtual_base() {}
};

struct non_virtual_base {
    int data;
    non_virtual_base() {}
    ~non_virtual_base() {}
};

int main() {
    std::cout << sizeof( virtual_base ) - sizeof( non_virtual_base ) << '\n';
    return 0;
}

But when i try this on cpp.sh http://cpp.sh/7o5av, without data (member variable) i get the size as 7 and with data size is coming out to be 12, so i failed to understand this behavior and any insights will be helpful and i know that size of empty class is 1 and in second with data member i expect this should come as 11 and not 7

PapaDiHatti
  • 1,841
  • 19
  • 26
  • ok then what could the reliable manner in which vptr size can be calculated programatically – PapaDiHatti Mar 23 '18 at 08:54
  • The issue is that the non-virtual class without data has size 1, even though it doesn't actually have anything in it, because size can never be 0. So `sizeof(non_virtual_base)` doesn't really show how much space is being used by the struct. – Barmar Mar 23 '18 at 08:56
  • 1
    Also beware of alignment issues: if the vTable is not exactly N*64 bit long the compiler might add some padding space in between members. – PhilipV Mar 23 '18 at 08:58
  • But an empty virtual class doesn't need to add 1 to the size, because the VPTR uses space (it's like an extra data member). This is why you need to use classes with at least 1 data member for this to work correctly. – Barmar Mar 23 '18 at 08:58
  • @PhilipV The vtable isn't in each object, it's a per-class data structure. The object just contains a pointer to it. – Barmar Mar 23 '18 at 08:59
  • @Barmar I understand that class with no data member have size = 1 that's why i assume that without data member size will come as 11 (12 for vptr and 1 for empty class) – PapaDiHatti Mar 23 '18 at 09:04
  • The size of a "virtual pointer" is the size of a pointer. It's simple to calculate, since all data pointers are the same size (this isn't a literal requirement, but in practice it's always true). So the size of the "virtual pointer" is `sizeof(void*)`. The code in the question calculates the difference in size between two classes that differ in the presence or absence of a virtual function. As the answers are suggesting, that difference is implementation-specific, and may well involve more than the presence or absence of a vtable pointer. – Pete Becker Mar 23 '18 at 13:16

3 Answers3

1

You get 7 with no data member becuase the empty class has size 1 so while the virtual one contains a pointer to the virtual table of size 8: 8-1=7.

When data members are involved the result you get depends on the actual type of the members. If you use int then the difference is 12 becuase the vptr has to be aligned to multiple of 8. This means that the int data member occupies bytes frm 0 to 4 and the vptr cannot be stored at byte 4 but starting from at byte 8. So the total size is 8+8=16 for the virtual struct. Try using a double and you will see that the difference is 8 as in the following code.

#include <iostream>

using namespace std;

struct virtual_base {
    double data;
    virtual_base() {}
    virtual ~virtual_base() {}
};

struct non_virtual_base {
    double data;
    non_virtual_base() {}
    ~non_virtual_base() {}
};

int main() {
    std::cout << sizeof( virtual_base ) - sizeof( non_virtual_base ) << '\n';
    return 0;
} 

try here: https://www.ideone.com/Ycpg64

Davide Spataro
  • 7,319
  • 1
  • 24
  • 36
  • So is there any reliable way to get size of vptr programatically – PapaDiHatti Mar 23 '18 at 09:05
  • Then why this question https://stackoverflow.com/questions/1604176/size-of-virtual-pointer-c if it's simply equal to pointer size – PapaDiHatti Mar 23 '18 at 09:11
  • Can i safely assume sizeof(void*) is same as size of vptr – PapaDiHatti Mar 23 '18 at 09:12
  • @Kapil What they're actually asking is how much extra space is used in the object when it has a virtual pointer. Because of alignment, the overhead may be more than just the size of the pointer. Why do you care about the pointer size itself? – Barmar Mar 23 '18 at 09:14
1

virtual_base only contains the vftable pointer which is apparently 8 bytes on your platform. virtual_base also has an int, and the vftable is aligned to 8 bytes. So it's something like:

4 bytes for int |  4 padding bytes  |  8 bytes for vftable pointer  | 
| x | x | x | x |    |    |    |    | v | v | v | v | v | v | v | v |

Please have a look at this. It might be helpful.

With int data member:

  • sizeof(virtual_base) is 16 [4(int)+ 4(padding) +8(vftable)] bytes
  • sizeof(non_virtual_base) is 4 bytes i.e. size of int.

Without int data member:

  • sizeof(virtual_base) is 8 bytes i.e the size of vftable pointer. There is no padding.
  • sizeof(non_virtual_base) without any data member is 1 byte.
Sumit Jha
  • 1,601
  • 11
  • 18
0

When you declare any function as virtual in C++, the class receives a hidden member vptr, which points to vtable. This is used to choose which function should actually be called when dynamic polymorphism is used.

Disclaimer: I'll be using results from online compiler you posted, with the options you set (C++14, O2 optimization)

We can easily see that empty class has sizeof equal to 1, and sizeof class with virtual functions is 8.
Then, with data member, class without virtual functions gets sizeof 4 and the one with virtual functions get sizeof 16.
We can also check that sizeof pointer type (for example int*) is equal to 8

So, what happens here: empty class gets assigned size 1 by default. But, a class with virtual functions must have vptr member, which itself is 8 bytes long. This gives you 8-1 = 7

When you add member to non-virtual class, it simply gets the size of all it's members, in this case it's int, so you have size 4.
For virtual class, you already have vptr of size 8 and add int of size 4 to it. Here the mechanism of structure alignment kicks in. Presumably, the system it's compiling allows for access only to bytes which are on offset of multiply of 8, so the compiler, to optimize access time, adds artifial padding bytes, which hold no data. Basically, object of your class would look like this:
[int(4B)|padding(4B)|vptr(8B)]
This results in size of class being 16. So we get 16 - 4 = 12.

Yksisarvinen
  • 18,008
  • 2
  • 24
  • 52