C++ Polymorphic memory cost - c++

I have this chunk of code:
#include <stdio.h>
class CoolClass {
public:
virtual void set(int x){x_ = x;};
virtual int get(){return x_;};
private:
int x_;
};
class PlainOldClass {
public:
void set(int x) {x_ = x;};
int get(){return x_;}
private:
int x_;
};
int main(void) {
printf("CoolClass size: %ld\n", sizeof(CoolClass));
printf("PlainOldClass size: %ld\n", sizeof(PlainOldClass));
return 0;
}
I'm getting a little bit confused because it says that the size of CoolClass is 16? How? Why? Even with the pointer to the vtable, shouldn't the size be 8? Size of oldclass is 4 as expected.
edit: I'm running Linux Mint 64 bit with g++ 4.6.3.

You can't assume anything about the sizes of anything other than char or unsigned char. If you're building on a 64 bit platform, int is likely still 4 bytes, but the size of the virtual table pointer would likely be 8, and the extra 4 bytes are for padding (so that the pointer is aligned to 8 bytes).
64-bit
+----+----+----+----+
| vp | vp | x_ | p |
+----+----+----+----+
vp - virtual table pointer
x_ - member
p - padding byte
32-bit
+----+----+
| vp | x_ |
+----+----+
vp - virtual table pointer
x_ - member
p - padding byte
Padding not required because the pointer is already aligned
As a test, you can try
class PlainOldClass {
private:
int* x_;
};
and its size would be 8.

My best guess is that you're compiling for a platform with 64-bit pointers. Then you'll need 8 bytes for the virtual pointer, probably 4 bytes for the int (some 64-bit platforms will also give that 8 bytes - but you say that sizeof (PlainOldClass) is 4, so that doesn't apply here), and another 4 to give the class the 64-bit alignment required by the pointer - giving a total of 16 bytes.

I think the cost is:
platform with 64-bit pointers, so 8 bytes for the virtual pointer
4 bytes for the int (but might be also 8 bytes on some platforms)
4 to give the class the 64-bit alignment required by the pointer
sum=16 bytes.

It's because your system may be 64-bit system, due to which it becomes,
8 bytes for vptr.
4 bytes for int
and additional 4 bytes padding to give 64-bit alignment to the pointer.
hence sum becomes 16.

Related

C++ - object padding intricacies

I'm trying to understand struct and class padding in depth, so I devised an example I considered more challenging than many of the examples I found in tutorials on the topic. I compiled it in an x64 machine with g++, without enabling any code optimization. My code is as follows:
class Example
{
private:
long double foobar; // 10 bytes + 6 padded bytes as double follows
double barfoo; // 8 bytes + 8 padded bytes
static float barbar; // didn't count as it's a static member
float *fooputs; // 8 bytes + 8 padded bytes
int footsa; // 4 bytes, stored in the padded portion of float
char foo; // 1 byte, stored in the padded portion of float
public:
int function1(int foo) { return 1; }
void function2(int bar) { foobar = bar; }
};
int main()
{
std::cout << sizeof(Example) << std::endl; // 48 bytes
return 0;
}
Although I see that the size of Example is 48 bytes, I expected it to be 37 bytes. The argumentation on my expectation is as follows:
foobar needs 10 bytes. As double follows, 6 more bytes are needed for padding.
barfoo needs 8 bytes, as it's a double. No need for padding, as mod(16,8) == 0
*fooputs needs 8 bytes, as it's a pointer in an x64 architecture. No need for padding, as mod(24,8) == 0
footsa needs 4 bytes as an int. No need for padding, as mod(32,4) == 0
foo needs 1 byte as a char. No need for padding.
As the result is different that the expected, I tried to understand how C++ evaluated the size of Example to 48 bytes by commenting in and out class members. So, besides of the argumentation for foobar I assumed the justifications I'm writing in my inline comments for each member.
Could anyone explain me how the size is evaluated to 48 bytes and if my justifications are correct?
You forget about the final padding. sizeof returns a number of bytes between two adjacent members in an array. In your case, alignof(long double) is very likely 16, therefore each Example instance requires to be at 16-byte aligned address.
Consequently, if you have first instance of Example at a 16-bytes aligned address A, and then there are 37 bytes required by members, the next Example instance cannot be stored at A + 37 bytes, but it needs to be stored at A + k * 16. The smallest possible k that satisfies k * 16 >= 37 is 3. Which finally gives you the number of bytes between two Example instances in an array 3 * 16 = 48, which is exactly sizeof(Example).

Why object with vptr is 12 bytes longer?

#include <iostream>
class B
{
public:
virtual void f() {std::cout<<"HI";}
int x;
};
class A
{
public:
void f() {std::cout<<"HI";}
int x;
};
int main () {
A a;
B b;
std::cout<<sizeof(a)<<" "<<sizeof(b);
return 0;
}
The output is
4 16
I expected it to be 8 bytes longer - vptr pointer. But for what the rest 4 bytes is used?
I found quite many forum posts (all from some years ago) where people discussed that object from the class with vprt is 4 or 8 bytes longer. I checked also on online C++ shell - output there is the same.
It seems that the pointer to the table of pointers to virtual functions has a size of 8 bytes. So the class B is aligned to the boundary of 8 bytes that is it has 4 additional padding bytes.

virtual pointer size varies based on class data members

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
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
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.
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.

How is memory alignment done usually? [duplicate]

This question already has an answer here:
Size of class with virtual function
(1 answer)
Closed 8 years ago.
Have a look at these 2 cases:
class A {
public:
int a;
A () { a = 10;}
void foo () {std::cout << "a = " << a << std::endl;}
};
Here sizeof(A) gives 4 bytes, which makes sense.
class A {
public:
int a;
A () { a = 10;}
virtual void foo () {std::cout << "a = " << a << std::endl;}
};
Here sizeof(A) gives 16 bytes as opposed to 12 bytes (4 + 8 for pointer).
Is there any explanation in terms of memory alignment for this ?
It's up to the compiler how virtual functions are implemented, but what's likely happening here is it wants/needs to align the 8-byte pointer to the virtual dispatch table on a multiple-of-8 memory address. Then there's either { 4 bytes a, 4 padding, 8 vdt pointer } or { 8 vdt pointer, 4 bytes a, 4 padding } - the latter's less obvious, but consider that arrays of A need to be contiguous and spaced per sizeof(A), so 12's rounded up to 16 given the 8-byte alignment.
FIRST in virtual dispatch table, visual functions need a pointer size , in 32 bits computer, the size of pointer is 4 bytes, int 64 bits it's 8 bytes. so I think your computer is 64 bits.
SECOND the sizeof(A) should consider the padding and memory alignment .
so 16 bytes is arranged like that: 4 bytes(int a) + 8 bytes(a virtual function pointer int 64 bits) + 4 bytes padding(because the max size of elements in A is the virtual function pointer which is 8 bytes, so the sizeof(A)should be the integral multiples of 8 bytes )

How much memory is allocated to an object of this class?

In an object of this example class
class example
{
public:
int x;
}
an object would be allocated 4 bytes of memory. As an int would take 4 bytes.
How much memory would be allocated to an object of the following class -
class node
{
public:
int data;
node *prev, *next;
};
The int would take four bytes, but what about the 'next' and 'prev' pointers? What about the total size of an object of the class?
The total size of the object is sizeof(int) + 2*sizeof(node*) + any padding the compiler might add between the members. Using sizeof(node) is the only portable and reliable way to find that out.
Pointers are of size 4 bytes on x86 system or 8 bytes on x64 system.
So your total size of node is 4 + 4 + 4 or 4 + 8 + 8 which is 12 bytes on x86 architecture, or 20 bytes on x64 architecture.
Because of padding however, on x64 architecture, the actual size of the class will be 24 bytes, because x64 architecture requires 8 byte alignment.
As mentioned by Oliver Charlesworth, you can also do std::cout << sizeof(node) << "\n";, which will tell you the exact size of class node