csse501 object-oriented development. chapter 12: implications of substitution in this chapter we...

21
CSSE501 Object-Oriented Development

Upload: alisha-parks

Post on 01-Jan-2016

213 views

Category:

Documents


0 download

TRANSCRIPT

CSSE501 Object-Oriented Development

Chapter 12: Implications of Substitution In this chapter we will investigate some of the

implications of the principle of substitution in statically typed object-oriented programming languages. In particular, we will consider: The impact on memory management The meaning of assignment The distinction between testing for identity and

testing for equality

Idealization of is-a Relationship A TextWindow is-a Window Because TextWindow is subclassed from Window, all

behavior associated with Windows is also manifest by instances of TextWindow.

Therefore, a variable declared as maintaining an instance of Window should be able to hold a value of type TextWindow.

Unfortunately, practical programming language

implementation issues complicate this idealized picture.

Memory Allocation - Stack and Heap Based Generally, programming languages use two different

techniques for allocation of memory. Stack-based allocation. Amount of space required is

determined at compile time, based on static types of variables. Memory allocation and release is tied to procedure entry/exit. Can be performed very efficiently.

Heap-based allocation. Amount of space used can be determined at run-time, based upon dynamic considerations. Memory allocation and release is not tied to procedure entry/exit, and either must be handled by user or by a run-time library (garbage collection). Generally considered to be somewhat less efficient.

The Problem with Substitution

class Window { public:

virtual void oops(); private:

int height; int width;

};

class TextWindow : public Window { public:

virtual void oops(); private:

char * contents; int cursorLocation;

};

Window x; // how much space to set aside? TextWindow y; x = y; // what should happen here?

Memory Strategies

How much memory should be set aside for the variable x ? (Minimum Static Space Allocation) Allocate the

amount of space necessary for the base class only. (C++)

(Maximum Static Space Allocation) Allocate the amount of space for the largest subclass.

(Dynamic Space Allocation) Allocate for x only the amount of space required to hold a pointer. (Smalltalk, Java)

Minimum Static Space Allocation

The language C++ uses the minimum static space allocation approach.

This is very efficient, but leads to some subtle difficulties.

What happens in the following assignment? Window x;

TextWindow y;

x = y;

Assigning a Larger Value to a Smaller Box

The Slicing Problem

The problem is you are trying to take a large box and squeeze it into a small space. Clearly this won't work. Thus, the extra fields are simply sliced off.

Question - Does this matter?

Answer - Only if somebody notices.

Solution - Design the language to make it difficult to

notice.

Rules for Member Function Binding in C++

The rules for deciding what member function to execute are complicated because of the slicing problem. With variables that are declared normally, the binding

of member function name to function body is based on the static type of the argument (regardless whether the function is declared virtual or not).

With variables that are declared as references or pointers, the binding of the member function name to function body is based on the dynamic type.

Illustration

void Window::oops() { printf("Window oops\n"); }

void TextWindow::oops() { printf("TextWindow oops %d\n", cursorLocation); }

TextWindow x; Window a;Window * b; TextWindow * c;

a = x; a.oops(); // executes Window version c = &x; c->oops(); // executes TextWindow version

Maximum Static Space Allocation

A different approach would be to allocate the Maximum amount of space you would ever need. Would nicely solve the slicing problem. Would often allocate unused space. Maximum amount of space not known until all classes

have been seen.

For this reason, not used in practice.

Dynamic Memory Allocation In the third approach, all objects are actually pointers. Only enough space for a pointer is allocated at

compile time. Actual data storage is allocated on the heap at run-

time. Used in Smalltalk, Object Pascal, and Objective-C,

Java. Requires user to explicitly allocate new objects and, in

some languages, explicitly free no longer used storage.

May also lead to pointer semantics for assignment and equality testing.

Meaning of Assignment? What does it mean when an instance of a class is

assigned to another variable?

Two possibilities: Copy semantics. x and y are independent of each other, a

change in one has no effect on the other. Pointer semantics. x and y refer to the same object, and

hence a change in one will alter the other.

class Box { public int value; }

Box x = new Box();x.value = 7; Box y = x; y.value = 12; // what is x.value?

Copy Semantics versus Pointer Semantics

If a value is indirectly accessed through a pointer, when an assignment is performed (or equality test is made), is the quantity assigned simply the pointer or is it the actual value?

Problems with Pointer Semantics If x is assigned to y and then changes are made to x,

are these changes reflected in y? If x is explicitly freed, what happens if the user tries to

access memory through y? In C++, programmer can make assignment (equality

testing) mean anything they want. Object Pascal, Java uses pointer semantics, no built-in

provision for copies. Smalltalk and Objective-C use pointer semantics, have

several techniques for making copies.

Equality and Identity A test for identity asks whether two variables refer to

exactly the same object. A test for equality asks whether two variables refer to

values that are equivalent. Of course, the meaning of equivalent is inheritently

domain specific. Object-oriented languages allow the programmer to control the meaning of the equality test by allowing the redefinition of a standard method. (for

example, equals in Java).

Paradoxes of Equality, Part 1 But child classes cannot change the type signature of

overridden methods. This means the argument must often be more general than one would like:

class Object { public boolean equals (Object right) {

... } }

class PlayingCard extends Object { public boolean equals (Object right) { ... // right must be object even if we are only... // interested in comparing cards to cards }

}

Paradoxes of Equality, Part 2 Because equality is a message sent to the left argument,

there is no guarantee that properties such as symmetry or transitivity are preserved:

class Foo { boolean equals (Object right) { ... }

}

Foo a, b;

if (a.equals(b)) // even if this is true

if (b.equals(a)) // no guarantee that this is true

Paradoxes of Equality, Part 3 And if you add inheritance into the mix, the possibilities

for paradoxical behavior increase even more. class Parent {

boolean equals (Object x) { ... } }

class Child extends Parent { boolean equals (Object x) { ... }

}

Parent p; Child c;

if (p.equals(c)) // will execute using the parent method if (c.equals(p)) // will execute using the child method

Chapter Summary We have explored the implications that result from the

inclusion of the principle of substitution in an object oriented programming language. Because values are not known until run time, you either have

complex semantics (as in C++) or objects are dynamic (as in Java and most other languages).

Because objects are dynamic, most object-oriented languages end up using a garbage collection system.

Dynamic semantics naturally lean to pointer semantics for assignment

Pointer semantics mean that equality and identity are two different concepts

Since equality is domain specific, the programmer must be free to redefine the meaning as appropriate

Because the programmer can redefine equality arbitrarily, there is no guarantee that semantics of equals is preserved.