Page 266 - Introduction to Microcontrollers Architecture, Programming, and Interfacing of The Motorola 68HC12
P. 266
8,7 Object-oriented Programming in C++ 243
template <class T> class Ostack : public Stack<T> { char Index;
public: Ostack(int i): Stack(i) { }/* constructor, calls base constructor */
operator T {) /* overloaded cast operator */
{if(index>(Ptr-Bottom)){Error=l;return 0;} return Ptr[-index];}
T operator = (T data) /* overloaded assignment operator */
{if(index>(Ptr-Bottom)){Error-Itreturn 0;} return Ptr[-index]=data;J
T operator [](char data){index=datajreturn *this; }/* index operator */
};
The overloaded index operator [ ] illustrates another C++ feature. This overloaded
operator is called whenever the compiler sees an index [ ] to the right of an object, as in
s [ 0 ], whether the object and index are on the left or right of an assignment statement
=. It executes the overloaded operator [ 3 before it executes the overloaded cast or
overloaded assignment operator. This overloaded operator simply stores what is inside the
square brackets, 0 in our example, in an object data member index. Then the following
overloaded cast or assignment operator can use this saved value to supply the offset to
the stack. Then s [ i ] would read or write the rth element from the top of the stack.
Now, whenever the compiler sees an object on the left side of an equal sign when it
has evaluated a number for the expression on the right side and it would otherwise be
unable to do anything correctly, the compiler looks at your declaration of the overloaded
assignment operator, to determine that the number will be pushed onto the stack, The
expression S = 1; will do the same thing as s. push (1);, and *Sptr - 1; will do
the same thing as Sptr->push{ 1);. Similarly, whenever the compiler sees an object
anywhere on the right side of an equal sign when it is trying to get a number and it
would otherwise be unable to do anything correctly, the compiler looks at your
declaration of the overloaded cast operator to determine that the number will be pulled
from the stack. The expression i = S; will do the same thing as i = S.pull();,
and i = *Sptr; will do the same thing as i = Sptr->pull();. Now if a stack S
returns a temperature in degrees centigrade, you can write an expression like degreeF =
(S * 9) / 5 + 32; or degreeF = (*Sptr * 9) / 5 + 32;, and the compiler
will pull an item from the stack each time it runs into the s symbolic name. While
overloading of operators isn't necessary, it provides a mechanism for simplifying
expressions to look like common algebraic formulas.
A derived class usually defines an overloaded assignment operator even if its base
class has defined an overloaded assignment operator in exactly the same way, because the
(Metrowerks) C++ compiler can get confused with the "=" sign. If SI and S2 are
objects of class Cstack<char>, then SI = S2; won't pop an item from S2 and push
it onto si, as we would wish when we use overloaded assignment and cast operators,
but "clones" the object, copying device2's contents into devicel as if the object
were a struct. That is, if SI's class's base class overrides "=" but SI's class itself
does not override "=", SI = S2; causes S2 to be copied into SI. However, if "=" is
u
overridden in si's class definition, =" is an overridden assignment operator, and SI =
S2; pops an item from S2 and pushes it onto SI. The derived class has to override
"=" to push data. The "=" operator, though useful, needs to be carefully handled. All
our derived classes explicitly define operator = if "=" is to be overridden.