Classes are structures, and then some - Embedded.com

Classes are structures, and then some

Last month I discussed Standard C's rules governing the alignment, padding and ordering of structure members.i For the most part, C code that defines and uses structures behaves the same when compiled and executed as C++. However, C++ generalizes structures into classes. A C++ class can have elements that a C structure cannot, such as access specifiers, member functions, static data members, and base classes. This month, I'll explain how some of these elements alter the physical layout of class objects.

The C++ Standard says a lot about classes, but hardly anything about structures. C++ treats structures as just classes declared in a slightly different way. According to the C++ Standard, “A structure is a class defined with the [keyword] struct ; its members and base classes … are public by default.”ii In a class defined with the keyword class , the members and bases are private by default.

For example, in C++, the structure definition:

struct widget{    char m1;    int m2;    char m3;};   

is actually equivalent to the class definition:

class  widget{public:      char m1;    int m2;    char m3;};   

As a class, widget has the same size and alignment as it does as a structure. Each class member has the same size, allocation order, alignment and padding as it does in the structure. When compiled for a target machine in which each int occupies four bytes aligned to an address that's a multiple of four, the compiler will insert three bytes of padding after each of the char members, as if the class had been defined as:

class widget{public:    char m1;    char padding_after_m1[3];          int m2;    char m3;    char padding_after_m3[3];      };   

The keyword public is one of three possible access-specifiers, the others being private and protected . The access-specifiers themselves don't occupy any data storage.

Non-virtual member functions don't occupy data storage either. (They do occupy code space.) Static data members do occupy data storage, but not in the objects of which they are members. Thus, adding either non-virtual member functions or static data members to a class doesn't alter the storage layout for objects of that class. For example, objects of a widget class defined as:

class widget{public:    widget();           // constructor     ~widget();          // destructor     char m1;    int m2;    char m3;    static int k;       // static data member };   

have the same storage layout with or without these new members (the ones highlighted) .

On the other hand, adding one or more virtual functions to a class that previously had none, as in:

class widget{public:    widget();    virtual  ~widget();  // virtual destructor    char m1;    int m2;    char m3;    static int k;};   

typically adds a hidden non-static data member of pointer type, called a vptr , thus increasing sizeof(widget) by the size of that pointer. The presence of the vptr has no impact on the size and alignment of any of the other data members, and typically no impact on the padding in the class.

Adding a base class to a class that previously had none, as in:

class widget: public gadget{public:    widget();    virtual  ~widget();  // virtual destructor    char m1;    int m2;    char m3;    static int k;};   

effectively adds a hidden non-static data member of the base class type, called the base class sub-object , thus increasing sizeof(widget) by the size of a gadget plus any additional padding that may be needed.

The C++ Standard says nothing about the placement of the base class sub-object and the vptr. Many compilers place the vptr at the beginning, followed by the base class sub-object and then the non-static data members. For example, the compiler might lay out the storage for objects of the derived widget class (just above) as if widget had been defined as:

class widget{public:    widget_vtable *vptr;    gadget base_class_subobject;    char m1;    int m2;    char m3;};   

Others place the base class sub-object before the vptr.

The compiler may insert padding as needed. If the gadget base class already has its own vptr, widget objects can use that vptr as their own, and the compiler can omit the allocation of a separate vptr in the widget itself.

The classes in all of my examples thus far have only public members. C++ has additional rules governing the placement of members with different accessibility. I'll discuss those rules next month.

Dan Saks is president of Saks & Associates, a C/C++ training and consulting company. For more information about Dan Saks, visit his website at www.dansaks.com. Dan also welcomes your feedback: e-mail him at . For more information about Dan .

Endnotes:
1. Saks, Dan, “Padding and rearranging structure members,” Embedded Systems Design , May 2009, p. 11.

2. ISO/IEC Standard 14882:2003(E), Programming languages–C++.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.