CPSC 427: Object-Oriented Programming
Michael J. Fischer
Storage Model Revisited
Object
parts
Recall that an object is a block of memory divided into parts, where each
part is an instance of the base class or is an instance of a data member.
We say that the parts are embedded in the “parent” or outer
object.
Embedded objects are actual objects in their own right. A constructor is
called when they are born, and their destructor is called when they are
about to die. They can be pointed at and used wherever an object of
their type is needed.
However, an embedded object is dependent on its parent and has the same lifetime and storage class as its parent.
Object parts (cont.)
Because an embedded object lives and dies with its parent, it can never
be explicitly deleted, even if it lives in the heap.
Rather, it is deleted when its parent is deleted. Neverthtless, its
destructor will be called just before it dies, just like for any other
object!
Note that embedding order and derivation order are opposites. If class Deriv is derived from class Base, then Base is the parent of Deriv in the class hierarchy, but an instance of Deriv has an instance of Base embedded within it.
Functions Revisited
Global vs. member functions A global function is one that takes zero or more explicit arguments.
Example: f(a, b) has two explicit arguments a and b.
A member function is one that takes an implicit argument along with zero or more explicit arguments.
Example: c.g(a, b) has two explicit arguments a and b and implicit argument c.
Example: d->g(a, b) has two explicit arguments a and b and implicit
argument *d.
Note that an omitted implicit argument defaults to (*this), which must make sense in the context.
Example: If g is a member function of class MyClass, then within MyClass, the call g(a, b) defaults to (*this).g(a,b) (or equivalently this->g(a,b)).
Defining global functions
There are three ways to define a global function.
Defining member functions
Placing a function declaration inside a class definition creates a member
function.
Its definition is considered to be “inside” the class, whether or not
it appears in the class or as an out-of-line function in a .cpp
file.
Example:
This defines a member function g with explicit parameters of type const int* and unsigned and implicit parameter of type const MyClass&.
Operator syntax
We have seen the operator keyword used to extend the meaning of
operators.
Each binary operator ⊕ corresponds to a function whose name is operator⊕, but the operator syntax a ⊕ b does not tell us whether to look for a global or a member function. Possibile meanings:
It could mean either, and the compiler sees if either one matches. If both match, it reports an ambiguity.
Operator extension as member function
Here’s a sketch for how one might go about defining a complex number class.
Operator extension as global function
We have seen one important example of a global operator extension
when we define the output operator on a new class.
Given the choice, it is preferable to use a member operator function.
We use a global form of operator<< because the left hand operator is of predefined type ostream, and we can’t add member functions to that class.
Prefix unary operator extensions
C++ has a number of prefix unary operators
*, -, ++, new, …
The corresponding operator functions are
operator*(), operator-(), operator++(),
Postfix unary operator extensions
C++ also has two postfix unary operators
++, --.
The corresponding operator functions are
operator++(int), operator--(int).
This is a special case that breaks all the normal rules, but it works since ++ and -- are not binary operators. The dummy int parameter should be ignored.
Ambiguous operator extensions
Compiler reports error: ambiguous overload for ’operator+’ in ’b + 5’.