Adding to @M.M answer, looks like even if you have public constructor
and setter
member function for class A
, the compiler still stores the class B
data members in padding area of class A
(I was trying to force the compiler to not to use tail padding of class A
but couldn't succeed).
A note can be found in class.mem/19 saying:
[ Note: Non-static data members of a (non-union) class with the same access control and non-zero size ([intro.object]) are allocated so that later members have higher addresses within a class object. The order of allocation of non-static data members with different access control is unspecified. Implementation alignment requirements might cause two adjacent members not to be allocated immediately after each other; so might requirements for space for managing virtual functions ([class.virtual]) and virtual base classes ([class.mi]). — end note ]
Adding more from this answer:
The standard requires members with the same access control to be grouped together in memory. That grouping decides how the object gets padded so changing it can/will change the size of the object.
And more from this answer:
The dsize, nvsize, and nvalign of these types are defined to be their ordinary size and alignment. These properties only matter for non-empty class types that are used as base classes. 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.
Thus, in your first example, A
is not a POD for layout purposes and its tail padding can be used for B::val
, but in your second example, it is a POD
, and its tail padding cannot be reused.
#include <iostream>
class A {
int val;
char c;
public:
A(int a, char b): val(a), c(b)
{
}
public:
void setC(int a)
{
c = a;
}
char getC(void) const
{
return c;
}
};
class B: public A {
char val;
public:
B(void): A(1,'2'), val('2')
{
}
public:
char getVal(void) const
{
return val;
}
};
struct C {
int val;
char c;
};
struct D: public C {
char val;
};
int main()
{
B a;
a.setC(2370);
std::cout << a.getVal() << " & " << a.getC() << std::endl;
std::cout << sizeof(B) << std::endl; // 8
std::cout << sizeof(D) << std::endl; // 12
return 0;
}
Outputs:
2 & B
8
12
To learn about memory order and alignment
for classes, see this.