Tuesday, June 5, 2007

C++ : Multiple Access Specifiers in a Class

On May 27th, Ramshankar in one of the C++ communities at Orkut, asked what seemed like a pretty innocuous question:

class TUid
{
public:
IMPORT_C TInt operator==(const TUid& aUid) const;
static inline TUid Null();
public:
TInt32 iUid;
};


What is the purpose of defining "public" section again? Is it for allowing:

TUid myType = { 0x01232423 };

Multiple public/protected/private sections are very much allowed in C++. In fact, they are seen in MFC wizard generated code. But, the real problem lay in not whether it was allowed, but why has this been allowed? As the C++ standard states that:

12. Nonstatic data members of a (non-union) class declared without an intervening access-specifier are allocated so that later members have higher addresses within a class object. The order of allocation of nonstatic data members separated by an access-specifier is unspecified (_class.access.spec_).

So, to find accurate (reliable) answer I had to email Bjarne, and this was his reply:

Consider

struct S {
public:
int a;
private:
int b;
public:
int c;
private:
int d;
};

Is the compiler allowed to allocate the private members next to each other? (the answer is yes).

The reason for the rule was early ideas of separating private data from public data for some implementations to be able to alleviate code evolution problems when the data layout changed.
For example if you allocated public before private, then adding a private member could be done without affecting the public intercase (after creation). As far as I know, no compiler has ever done that.

However, some compilers do use rearrangement to create more compact layouts. For example:

struct SS {
char a;
public:
int b;
public:
char c;
public:
int d;
public:
char e;
};

If members are allocated in declaration order, the size will be 5 words, but you can (legally) reorder to get 3 words (assuming a 4-byte word).

Personally, I have never found this useful.

I'll leave as an exercise how to reorder to get 3 words . <(^_^)>

But, a mystery still remains as André asked:

Can you write any piece of strictly conforming code for which 9p12 (the above stated standard snippet) makes ANY difference for a non-POD (Plain Old Data) type?

Put another way (this is a different formulation of the same question):
Can you write any piece of code that uses 9p12 (the fact that the order is specified) for a non-POD type, without invoking undefined behaviour?

Can you?!