Previous section   Next section

Imperfect C++ Practical Solutions for Real-Life Programming
By Matthew Wilson
Table of Contents
Chapter 35.  Properties


35.2. Implementation Options

What we're after is the syntactic appearance of a member variable, but with the resultant access translating to method calls. Well, thankfully C++, as we've proven many times in this book already, has the supporting infrastructure we need, in the form of implicit conversion operators, user-definable assignment operators, references, and templates. (We use some less noble parts of the language as well, but I don't want to spoil a good story just yet.)

35.2.1 A Taxonomy of Property Implementation Options

I'd first like to highlight the options with which one might approach the implementation of properties. First, to achieve the syntax of a member variable, we need a member variable. After all, operator .() is not overloadable [Stro1994]. Beyond this, however, there are several alternatives.

In some cases, the property's notional value is backed up by an actual member variable (field) within the containing class: these are called Field Properties (see section 35.3), and provide read or write access control only. Conversely, the value may be implemented in terms of arbitrarily complex methods, providing validation and/or proxying behavior; these are called Method Properties (see section 35.4). Naturally, the two can be combined (see section 35.4).

Where properties are backed by actual member variables, there are two further refinements. The member variable may be provided within the property member itself; these are Internal Properties. Alternatively, the member variable may already exist as part of the containing class, and therefore outside the property variable; these are External Properties.

The permutations of read and/or write, field and/or method, internal or external, will be examined in the sections that follow. They represent various trade-offs between portability, (space and time) efficiency, and even legality.

35.2.2 EMO?

Before we get stuck into the details of the various implementations, I want to discuss an issue that underpins the imperfect nature of some of these property solutions.

We saw in section 12.3 the officially recognized Empty Base Optimization, and the unofficial, but widely implemented, Empty Derived Optimization. Consider the following case, where empty classes are used as member variables.



struct empty


{};





struct emptyish


{


  empty e1;


  empty e2;


  empty e3;


  int   i1;


};



Since e1, e2, and e3 are empty, it would be nice if the compiler could allocate the storage for instances of emptyish based on the nonempty fields, such that sizeof(emptyish) == sizeof(int). This would be an Empty Member Optimization (EMO).

At first glance it seems like this shouldn't be a problem. After all, since they're empty, they don't need any space. Further, having no member variables, it wouldn't really matter if the address of any of the empty members were the same. However, consider what would happen in the following case:



struct emptyish2


{


  empty ae[3];


  int   i1;


};



The three empty members are now in an array. Clearly to allocate zero storage to them will now lead to a contradiction: &ae[0] == &ae[1] == &ae[2] just doesn't make sense. So, we have the rule that the members of any aggregate have non-zero storage.


      Previous section   Next section