C++ size of virtual inheritance classes - c++

Here is my code:
#include <iostream>
using namespace std;
class MyClass{
int mem1;
float mem2;
};
class MyKids: public virtual MyClass{
int cmem1;
int cmem2;
};
class MyLawKids:public virtual MyClass{
int lmem1;
};
class MyGrands:public MyKids, public MyLawKids{
int gmem1;
};
int main(){
cout << "\n Size of MyClass: " << sizeof(MyClass) << " bytes" << endl ;
cout << " Size of MyKids: " << sizeof(MyKids) << " bytes" << endl ;
cout << " Size of MyLawKids: " << sizeof(MyLawKids) << " bytes" << endl ;
cout << " Size of MyGrands: " << sizeof(MyGrands) << " bytes" << endl ;
return 0;
}
Result:
Size of MyClass: 8 bytes
Size of MyKids: 20 bytes
Size of MyLawKids: 16 bytes
Size of MyGrands: 32 bytes
Process returned 0 (0x0) execution time : 0.094 s
Press any key to continue.
I think the size of MyGrands class its should be 36 bytes = (sizeof(MyKids + MyLawKids + int gmem1) - 4 bytes of virtual table pointer).
Why my result shown me "Size of MyGrands: 32 bytes" ?

MyGrands contains only one instance of MyClass due to MyKids and MyLawKids inheriting virtually from MyClass. Hence your calculation is wrong, by double counting the size of MyClass. By the way, in general you should not rely on a naive addition of the pieces being the same as the size of the whole. The compiler can add padding.

Related

Memory allocation when declaring a series of class objects in C++

class Data {
double a, b, c;
};
int main() {
Data x, y, z;
cout << sizeof(x) << endl;
cout << &x << " " << &y << " " << &z << endl;
}
The output of the above code was:
24
0x7ffd911f5640 0x7ffd911f5660 0x7ffd911f5680
Here's my question: The Data class object needs only 24 bytes of space, then why the compiler allocates 32 bytes (memory spaces between 0x7ffd911f5640 and 0x7ffd911f5660) for each object instead?

Packing non-POD type: no warning, unexpected size?

Here is a rather contrived series of types. A2 is just a non-POD version of A:
template <size_t N>
struct A {
char data[N];
} __attribute__((packed));
template <size_t N>
struct A2 {
A2() { // actual body not significant
memset(data, 0, N);
}
char data[N];
} __attribute__((packed));
template <template <size_t> class T>
struct C {
T<9> a;
int32_t i;
T<11> t2;
} __attribute__((packed));
//}; // oops, forgot to pack
template <template <size_t> class T>
struct B : C<T> {
char footer;
} __attribute__((packed));
As-is, sizeof(B<A>) == sizeof(B<A2>) == 25. I get no warnings compiling with -Wall -pedantic (this is gcc 4.9.0).
But now, let's say I forgot to pack C. Now I get:
sizeof(B<A>) == 32
sizeof(B<A2>) == 28
Still no warnings. What happened here? How is B<A2> smaller than B<A>? Is this just undefined behavior due to A2 not being POD?
If I reorganize B to look have a C<T> member instead of inheriting from it, then and only then do I get a warning:
ignoring packed attribute because of unpacked non-POD field ‘C B::c’
[Update] In response to IdeaHat's comments, here are some other the offsets:
B<A> B<A2>
a 0 0
i 12 12
t2 16 16
footer 28 27
And
sizeof(C<A>) == 28
sizeof(C<A2>) == 28
[Update 2] I see the same behavior on clang with respect to the offsets, except that sizeof(B<A>) is 29 instead of 32.
That behaviour is due to the compiler is allowed to apply optimizations in a non-POD type (e.g.: C<A2>).
It doesn't apply to POD types (e.g.:C<A>).
I've found this related question with a very helpful answer by Kerrek SB:
When extending a padded struct, why can't extra fields be placed in the tail padding?
On the other hand, you can force this optimization regardless the POD'ness with the -fpack-struct option in GCC. Although not recommended, it's useful for the example.
#include <stdint.h>
#include <stdio.h>
struct C {
int16_t i;
char t[1];
};
struct C2 {
C2() {}
int16_t i;
char t[1];
};
template <class T>
struct B : T {
char footer;
};
int main(void) {
printf("%lu\n", sizeof(B<C>));
printf("%lu\n", sizeof(B<C2>));
return 0;
}
If you compile it with -fpack-struct (gcc-4.7):
sizeof(B<C>) == sizeof(B<C2>) == 4
If not:
sizeof(B<C>) == 6
sizeof(B<C2>) == 4
From man gcc (4.7):
-fpack-struct[=n]
Without a value specified, pack all structure members together
without holes.
When a value is specified (which must be a small power of two),
pack structure members according to this value, representing
the maximum alignment (that is, objects with default alignment
requirements larger than this will be output potentially
unaligned at the next fitting location.
Warning: the -fpack-struct switch causes GCC to generate code
that is not binary compatible with code generated without that
switch.
Additionally, it makes the code suboptimal. Use it to conform
to a non-default application binary interface.
As you can see, when a class is POD and it acts as the base class of another class, that base is not packed unless you force it. i.e.: it doesn't use the tail padding of the base.
In the particular case of the C++ ABI which GCC uses, there's a discussion about this:
It looks like the ABI document meant to require the reuse of tail-padding
in non PODs, but it doesn't actually say that.
Consider this case, as the canonical example:
struct S1 {
virtual void f();
int i;
char c1;
};
struct S2 : public S1 {
char c2;
};
I think the ABI meant to say that you put "c2" in the tail padding for
S1. (That is what G++ implements, FWIW.)
Take a look at what the Itanium ABI C++ (this is the one GCC uses) specifies about tail padding:
This ABI uses the definition of POD only to decide whether to allocate objects in the tail-padding of a base-class subobject. While the standards have broadened the definition of POD over time, they have also forbidden the programmer from directly reading or writing the underlying bytes of a base-class subobject with, say, memcpy. Therefore, even in the most conservative interpretation, implementations may freely allocate objects in the tail padding of any class which would not have been POD in C++98. This ABI is in compliance with that.
In addition, here's the reason why the C++ ABI doesn't use the tail padding of POD objects:
We ignore tail padding for PODs because an early version of the standard did not allow us to use it for anything else and because it sometimes permits faster copying of the type.
In your example, C<A> is POD and for that reason the ABI is not using its tail padding when the type act as the base class of B<A>.
For that, C<A> remains with padding (and occupying 28 bytes as it) and footer occupies 4 bytes respecting the alignment.
Finally, I want to share the code I used to make some test, previously to find a proper answer. You can find it useful in order to see what the compiler ABI does with the objects in C++(11) (GCC).
Test code
#include <iostream>
#include <stddef.h>
struct C {
int16_t i;
char t[1];
};
struct C2 {
C2() {}
int16_t i;
char t[1];
};
template <class T>
struct B : T {
char footer;
};
int main(void) {
std::cout << std::boolalpha;
std::cout << "standard_layout:" << std::endl;
std::cout << "C: " << std::is_standard_layout<C>::value << std::endl;
std::cout << "C2: " << std::is_standard_layout<C2>::value << std::endl;
std::cout << "B<C>: " << std::is_standard_layout<B<C>>::value << std::endl;
std::cout << "B<C2>: " << std::is_standard_layout<B<C2>>::value << std::endl;
std::cout << std::endl;
std::cout << "is_trivial:" << std::endl;
std::cout << "C: " << std::is_trivial<C>::value << std::endl;
std::cout << "C2: " << std::is_trivial<C2>::value << std::endl;
std::cout << "B<C>: " << std::is_trivial<B<C>>::value << std::endl;
std::cout << "B<C2>: " << std::is_trivial<B<C2>>::value << std::endl;
std::cout << std::endl;
std::cout << "is_pod:" << std::endl;
std::cout << "C: " << std::is_pod<C>::value << std::endl;
std::cout << "C2: " << std::is_pod<C2>::value << std::endl;
std::cout << "B<C>: " << std::is_pod<B<C>>::value << std::endl;
std::cout << "B<C2>: " << std::is_pod<B<C2>>::value << std::endl;
std::cout << std::endl;
std::cout << "offset:" << std::endl;
std::cout << "C::i offset " << offsetof(C, i) << std::endl;
std::cout << "C::t offset " << offsetof(C, t) << std::endl;
std::cout << "C2::i offset " << offsetof(C2, i) << std::endl;
std::cout << "C2::t offset " << offsetof(C2, t) << std::endl;
B<C> bc;
std::cout << "B<C>.i: " << (int)(reinterpret_cast<char*>(&bc.i) - reinterpret_cast<char*>(&bc)) << std::endl;
std::cout << "B<C>.t: " << (int)(reinterpret_cast<char*>(&bc.t) - reinterpret_cast<char*>(&bc)) << std::endl;
std::cout << "B<C>.footer: " << (int)(reinterpret_cast<char*>(&bc.footer) - reinterpret_cast<char*>(&bc)) << std::endl;
B<C2> bc2;
std::cout << "B<C2>.i: " << (int)(reinterpret_cast<char*>(&bc2.i) - reinterpret_cast<char*>(&bc2)) << std::endl;
std::cout << "B<C2>.t: " << (int)(reinterpret_cast<char*>(&bc2.t) - reinterpret_cast<char*>(&bc2)) << std::endl;
std::cout << "B<C2>.footer: " << (int)(reinterpret_cast<char*>(&bc2.footer) - reinterpret_cast<char*>(&bc2)) << std::endl;
std::cout << std::endl;
std::cout << "sizeof:" << std::endl;
std::cout << "C: " << sizeof(C) << std::endl;
std::cout << "C2: " << sizeof(C2) << std::endl;
std::cout << "DIFFERENCE:\n";
std::cout << "B<C>: " << sizeof(B<C>) << std::endl;
std::cout << "B<C2>: " << sizeof(B<C2>) << std::endl;
std::cout << "B<C>::C: " << sizeof(B<C>::C) << std::endl;
std::cout << "B<C2>::C: " << sizeof(B<C2>::C2) << std::endl;
std::cout << std::endl;
std::cout << "alignment:" << std::endl;
std::cout << "C: " << std::alignment_of<C>::value << std::endl;
std::cout << "C2: " << std::alignment_of<C2>::value << std::endl;
std::cout << "B<C>: " << std::alignment_of<B<C>>::value << std::endl;
std::cout << "B<C2>: " << std::alignment_of<B<C2>>::value << std::endl;
std::cout << "B<C>::C: " << std::alignment_of<B<C>::C>::value << std::endl;
std::cout << "B<C2>::C2: " << std::alignment_of<B<C2>::C2>::value << std::endl;
std::cout << "B<C>.i: " << std::alignment_of<decltype(std::declval<B<C>>().i)>::value << std::endl;
std::cout << "B<C>.t: " << std::alignment_of<decltype(std::declval<B<C>>().t)>::value << std::endl;
std::cout << "B<C>.footer: " << std::alignment_of<decltype(std::declval<B<C>>().footer)>::value << std::endl;
std::cout << "B<C2>.i: " << std::alignment_of<decltype(std::declval<B<C2>>().i)>::value << std::endl;
std::cout << "B<C2>.t: " << std::alignment_of<decltype(std::declval<B<C2>>().t)>::value << std::endl;
std::cout << "B<C2>.footer: " << std::alignment_of<decltype(std::declval<B<C2>>().footer)>::value << std::endl;
return 0;
}
Output with gcc-4.7
standard_layout:
C: true
C2: true
B<C>: false
B<C2>: false
is_trivial:
C: true
C2: false
B<C>: true
B<C2>: false
is_pod:
C: true
C2: false
B<C>: false
B<C2>: false
offset:
C::i offset 0
C::t offset 2
C2::i offset 0
C2::t offset 2
B<C>.i: 0
B<C>.t: 2
B<C>.footer: 4
B<C2>.i: 0
B<C2>.t: 2
B<C2>.footer: 3
sizeof:
C: 4
C2: 4
DIFFERENCE:
B<C>: 6
B<C2>: 4
B<C>::C: 4
B<C2>::C: 4
alignment:
C: 2
C2: 2
B<C>: 2
B<C2>: 2
B<C>::C: 2
B<C2>::C2: 2
B<C>.i: 2
B<C>.t: 1
B<C>.footer: 1
B<C2>.i: 2
B<C2>.t: 1
B<C2>.footer: 1

Why is the size of an empty class not zero in C++? [duplicate]

This question already has answers here:
C++: What is the size of an object of an empty class?
(17 answers)
Why sizeof(Derived4) is 8 byte? I think it should be 5 bytes
(2 answers)
Closed 8 years ago.
I was going through some of the c++ puzzles.
And I have seen this question.
#include <iostream>
using namespace std;
class Empty
{};
class Derived1 : public Empty
{};
class Derived2 : virtual public Empty
{};
class Derived3 : public Empty
{
char c;
};
class Derived4 : virtual public Empty
{
char c;
};
class Dummy
{
char c;
};
int main()
{
cout << "sizeof(Empty) " << sizeof(Empty) << endl;
cout << "sizeof(Derived1) " << sizeof(Derived1) << endl;
cout << "sizeof(Derived2) " << sizeof(Derived2) << endl;
cout << "sizeof(Derived3) " << sizeof(Derived3) << endl;
cout << "sizeof(Derived4) " << sizeof(Derived4) << endl;
cout << "sizeof(Dummy) " << sizeof(Dummy) << endl;
return 0;
}
When I run the code.The results are found to be:
sizeof(Empty) 1
sizeof(Derived1) 1
sizeof(Derived2) 4
sizeof(Derived3) 1
sizeof(Derived4) 8
sizeof(Dummy) 1
Can some one explain why the sizeof(Derived2) and sizeof(Derived4) are 4 and 8 respectively?
Thanks.

How to write a code to calculate and display the stuffing / padding byte

See the below code:
#include <iostream>
using namespace std;
class temp
{
public:
int x; //8
char z; //4
int y;
};
int main()
{
temp tmp;
cout << "Total size=" << sizeof(tmp) << endl;
cout << "x=" << sizeof(tmp.x) /*<< "y" << sizeof(tmp.y) */<< "z=" << sizeof(tmp.z) << endl;
return 0;
}
Output comes:
Total size: 12
x=4 y=4 z=1
My question is: for different data types, stuffing / padding bytes will be placed. However how can we identify and calculate the total size of stuffing/padding bytes or how can we write code (generally not specific to this code) ?

where did my 4 bytes go?

#include <iostream>
#include <cstdlib>
using std::cout;
class A
{
public :
A() { cout << "A()" << this << "\n";}
~A() { cout << "~A()" << this << "\n";}
//void func() { }
virtual void debug(int a) { cout << "A::debug";}
private :
int a;
};
class A1 : public A
{
public :
A1() { cout << "A1()"<< this << "\n";}
~A1() { cout << "~A1()"<< this << "\n";}
private :
int a1;
};
class A2 : public A
{
public :
A2() { cout << "A2()"<< this << "\n";}
~A2() { cout << "~A2()"<< this << "\n";}
private :
int a2;
};
class B : public A1, public A2
{
public :
B() { cout << "B()"<< this << "\n";}
~B() { cout << "~B()"<< this << "\n";}
void debug() { cout << "B::debug()"; }
private :
int a3;
};
int main()
{
cout << "sizeof(int)" << sizeof(int) << "\n";
cout << "sizeof(void*)" << sizeof(void*) << "\n";
cout << "sizeof(A): " << sizeof(A) << "\n";
cout << "sizeof(A1): " << sizeof(A1) << "\n";
cout << "sizeof(A2): " << sizeof(A2) << "\n";
cout << "sizeof(B): " << sizeof(B) << "\n";
B b;
b.debug();
}
output :
sizeof(int)4
sizeof(void*)4
sizeof(A): 8
sizeof(A1): 12
sizeof(A2): 12
**sizeof(B): 28**
A()0x28fef4
A1()0x28fef4
**A()0x28ff00**
A2()0x28ff00
B()0x28fef4
B::debug()~B()0x28fef4
~A2()0x28ff00
~A()0x28ff00
~A1()0x28fef4
~A()0x28fef4
Both A1 and A2 are 4(vtbl) + 4(A'sint) + 4(respective int) = 12 bytes but B is 28 bytes
I know its not guaranteed but what could be the possible use of those 4 bytes...I dont see any padding issues ? Can anyone point out what am I missing ?
sizeof(A): 8
The type A has a member of type int which in your platform is 4 bytes. It also has a virtual function, which means that a vptr (virtual table pointer) is allocated for each object of your class, the size of it is another 4 bytes.
**sizeof(B): 28**
B contains one object of type A1 (12 bytes), and an object of type A2 (another 12 bytes) and it adds another int for a total of 12+12+4 = 28 bytes. This is quite straightforward.
machine word size alignment of data items within structures.
See structure packing for more information.
Multiple inheritance will produce implementation-specific memory layouts of possibly different sizes.
Virtual tables and virtual pointers for multiple virtual inheritance and type casting