Why can it be dangerous to use this POD struct as a base class?
In theory, the compiler can lay out base classes differently. C++03 §10 paragraph 5 says:
A base class subobject might have a layout (3.7) different from the layout of a most derived object of the same type.
As StackedCrooked mentioned, this might happen by the compiler adding padding to the end of the base class A
when it exists as its own object, but the compiler might not add that padding when it's a base class. This would cause A::clear()
to overwrite the first few bytes of the members of the subclass.
However in practice, I have not been able to get this to happen with either GCC or Visual Studio 2008. Using this test:
struct A
{
void clear() { memset(this, 0, sizeof(A)); }
int age;
char type;
};
struct B : public A
{
char x;
};
int main(void)
{
B b;
printf("%d %d %d\n", sizeof(A), sizeof(B), ((char*)&b.x - (char*)&b));
b.x = 3;
b.clear();
printf("%d\n", b.x);
return 0;
}
And modifying A
, B
, or both to be 'packed' (with #pragma pack
in VS and __attribute__((packed))
in GCC), I couldn't get b.x
to be overwritten in any case. Optimizations were enabled. The 3 values printed for the sizes/offsets were always 8/12/8, 8/9/8, or 5/6/5.
The compiler is likely to add padding bytes to A. So sizeof(A)
extends beyond char type
(until the end of the padding). However in case of inheritance the compiler might not add the padded bytes. So the call to memset
will overwrite part of the subclass.
In addition to the other notes, sizeof
is a compile-time operator, so clear()
will not zero out any members added by derived classes (except as noted due to padding weirdness).
There's nothing really "subtle" about this; memset
is a horrible thing to be using in C++. In the rare cases where you really can just fill memory with zeros and expect sane behaviour, and you really need to fill the memory with zeros, and zero-initializing everything via the initializer list the civilized way is somehow unacceptable, use std::fill
instead.