520 likes | 598 Views
COSC2767: Object-Oriented Programming. Haibin Zhu, Ph. D. Associate Professor of CS, Nipissing University. Lecture 5. Static, Dynamic Behavior and Substitution. Review or Refresh: C++ Pointers and Dynamic Allocation. Constant Pointers Pointer Conversions Allocating Memory.
E N D
COSC2767: Object-Oriented Programming Haibin Zhu, Ph. D. Associate Professor of CS, Nipissing University
Lecture 5 Static, Dynamic Behavior and Substitution
Review or Refresh:C++ Pointers and Dynamic Allocation • Constant Pointers • Pointer Conversions • Allocating Memory
Constant Pointers • The object can not be modified when use this pointer to access. int n = 0; const int * cp = &n; * cp = 30; // Error! n = 30; //OK! //ex5-1.cpp
Constant Pointers • The same for parameters size_t strlen(const char * str) const char * aStr = “ABCDEFG”; char name [] = “Johnson”; unsigned n; n = strlen(aStr); n = strlen(name); • Can not pass a pointer to a constant to a function having a parameter that is a pointer to a non-constant
Example char * strcpy(char * dest, const char * source); … const char * des; const char * sou; strcpy(des, sou); //error!
Const-Qualified Pointers char message[80]; char *const sp = message; sp++; strcpy(sp, “A new message”); //error //OK
Const-Qualified Pointers char message[80]; const char *const sp = message; sp++; strcpy(sp, “A new message”); • //ex5-2.cpp //error //error
Four ways to declare a pointer char *p1 = message; char *const p2 = message; const char *p3 = message; const char *const p4 = message;
Functions returning Pointers to Constants • The variable receiving the returned value should be also a pointer to a constant. class Student{ public: const char * GetName() const; // … }
Functions returning Pointers to Constants Student s; char * ncName = S.GetName(); const char * cName = S.GetName(); //error //OK
What do the terms Static and Dynamic Mean? • In Programming languages: • Static almost always means fixed or bound at compile time, and cannot thereafter be changed. • Dynamic almost always means not fixed or bound until run time, and therefore can change during the course of execution.
Static and Dynamic Typing • In a statically typed programming language (C++, Java or Pascal), for example, variables have declared typed -- fixed at compile time. • In a dynamically typed programming language (Smalltalk or CLOS), a variable is just a name. Types are associated with values, not variables. A variable can hold different types during the course of execution.
Static Class and Dynamic Class • In a statically typed language we say the class of the declaration is the static class for the variable, while the class of the value it currently holds is the dynamic class. Most statically typed OO languages constrain the dynamic class to be a child class of the static class. • var • pet : Mammal; • fido : Dog • begin • pet := fido; // static class is Mammal, dynamic class is Dog • end;
Importance of Static Class • In a statically typed object-oriented language, the legality of a message is determined at compile time, based on the static class. • A message can produce a compile error, even if no run-time error could possibly arise: • class Mammal { } • class Dog extends Mammal { • void speak() { System.out.println("woof"); } • } • Mammal pet = new Dog; • pet.speak(); // will generate error, Mammals don't speak • //ex5-3.cpp, //Lect5 - java
Reverse Polymorphism • Polymorphism says we can assign a value from a child class to an instance of the parent class, but can this assignment then be reversed? Under what conditions? • var • pet : Mammal; • fido : Dog; • felice : Cat; • begin • pet := fido; // legal • fido := pet; // is this legal? • end; • This is known as the problem of reverse polymorphism.
Two aspects of reverse polymorphism • There are two specific problems associated with the question of reverse polymorphism. • The problem of identity - can I tell if a value declared as an instance of a parent class actually holds a value from a subclass. • The task of assignment - can I then assign the value from the parent class to a variable declared as the subclass. • In some languages mechanisms are provided to address these two problems together, while in other languages they are separated.
The Container Problem • The task of reverse polymorphism is often encountered in connection with a collection of values - we have a list of items from the parent class (say a list of Mammals), and when we extract a value we need to know if it is a more specific type. • Generally occurs in languages with a single inheritance tree, where the only type we may have associated with a value is the class ``Object''. • Solving this problem generally requires values to have ``self knowledge'' of their own type. In some languages they do, in some languages values do not.
Static and Dynamic Method Binding • Should the binding for information be associated with the static class of a variable or the dynamic class. • Alice holds a small Mammal - asks Bill ``does this animal give birth to live young''. • Static answer - All mammals give birth to live young - therefore yes. • What if the Mammal is a platypus? Dynamic answer - Platypus lay eggs, therefore no. • Even statically typed OOP languages can use dynamic binding. But may use static type to determine legality of operation.
Dynamic Method Binding • In many languages dynamic binding is the default (Java). • If a child class overrides a method in the parent, using the same type signature, then the selected method will be determined by the dynamic type. • In other languages (C++, Delphi, C#) the programmer must indicate which methods are dynamically bound and which are statically typed. • In C++ and C#, for example, this is done using the virtual keyword.
Merits of Static versus Dynamic Method Binding • Arguments concerning static versus dynamic binding mirror those concerning static versus dynamic typing. • Efficiency - static binding uses least CPU cycles, dynamic binding requires more time. • Error detection - static binding permits errors to be caught at compile time rather than run-time. • Flexibility - dynamic binding permits greater flexibility, static binding creates rigidity and inhibits reuse.
Constructors and Destructors-Static allocation class Student { private: int id; int credit; int GPA; char name[30]; public: Student (){ id = 0; credit = 0; GPA =0; strcpy(name, ""); cout<<"construct a student"<<endl; }; int getID() {return id;} int getCredit() {return credit;} int getGPA() {return GPA;} void setID(int anID) {id=anID;} void setCredit(int aCredit) {credit=aCredit;} void setGPA(int aGPA) {GPA=aGPA;} char * getName() {return name;} void setName(char *nm) {strcpy(name,nm);} ~Student() { cout<<"delete a student."<<endl;}; };//ex5-4.cpp
Constructors and Destructors-Dynamic allocation int getID() {return id;} int getCredit() {return credit;} int getGPA() {return GPA;} void setID(int anID) {id=anID;} void setCredit(int aCredit) {credit=aCredit;} void setGPA(int aGPA) {GPA=aGPA;} char * getName() {return name;} void setName(char *nm) {strcpy(name,nm);} ~Student() {delete name; cout<<"delete a student."<<endl;}; };//ex5-5.cpp class Student { private: int id; int credit; int GPA; char *name; public: Student (){ id = 0; credit = 0; GPA =0; name = new char [30]; strcpy(name, "no name"); cout<<"construct a student"<<endl; };
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; • }; • main() • { 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 ? • 1. (Minimum Static Space Allocation) Allocate the amount of space necessary for the base class only. (C++) • 2. (Maximum Static Space Allocation) Allocate the amount of space for the largest subclass. • 3. (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; //???
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. • 1. 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). • 2. 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 if the function is declared as virtual, and the static type if not.
C1 o1 C10 o10; … p=&o10; p->f1() C10 o10; … o1=o10; o1.f1(); YES Polymorphism! NO Polymorphism! Ex5-6.cpp
Examples in C++ • class Animal { • public: • virtual void speak () { cout << "Animal Speak !\n"; } • void reply () { cout << "Animal Reply !\n"; } • }; • class Dog : public Animal { • public: • virtual void speak () { cout << "woof !\n"; } • void reply () { cout << "woof again!\n"; } • }; • class Bird : public Animal { • public: • virtual void speak () { cout << "tweet !\n"; } • }; • Ex5-7.cpp Animal a; Dog b; b.speak(); //woof ! a = b; a.speak(); //woof? Bird c; c.speak(); //tweet ! a = c; a.speak(); //tweet ?
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? • class Box { • public int value; • } • Box x = new Box(); • x.value = 7; • Box y = x; • y.value = 12; // what is x.value? • 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.
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.
An Old Story Concerning Equality A man walks into a pizza parlor and sits down. A waiter comes to the table and asks the man what he would like to order. The man looks around the room, then points to the woman sitting at the next table, and says ``I'll have what she is eating.'' The waiter thereupon walks to the woman’s table, picks up the half-eaten pizza from in front of her, and places it before the startled customer. • A classic confusion between equality and identity.
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 inherently 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 childs method
Pass ``by value'‘ in Java • void f() { • int n = 1; • Pair p = new Pair(); • p.x = 2; p.y = 3; • System.out.println(n); // prints 1 • System.out.println(p.x); // prints 2 • g(n,p); • System.out.println(n); // still prints 1 • System.out.println(p.x); // prints 100 • } • void g(int num, Pair ptr) { • System.out.println(num); // prints 1 • num = 17; // changes only the local copy • System.out.println(num); // prints 17 • System.out.println(ptr.x);// prints 2 • ptr.x = 100; // changes the x field of caller's Pair • ptr = null; // changes only the local ptr • }//FunctionCalls.java
Array in Java int x = 3; // a value int[] a; // a pointer to an array object; initially null int a[]; // exactly the same thing a = new int[10]; // now a points to an array object a[3] = 17; // accesses one of the slots in the array a = new int[5]; // assigns a different array to a // the old array is inaccessible (and so // is garbage-collected) int[] b = a; // a and b share the same array object System.out.println(a.length); // prints 5 //Array.java
String in Java (1) • String s = "hello"; • String t = "world"; • System.out.println(s + ", " + t); // prints "hello, world" • System.out.println(s + "1234"); // "hello1234" • System.out.println(s + (12*100 + 34)); // "hello1234" • System.out.println(s + 12*100 + 34); // "hello120034" (why?) • System.out.println("The value of x is " + x); // will work for any x • System.out.println("System.out = " + System.out); • // "System.out = java.io.PrintStream@80455198" • String numbers = ""; • for (int i=0; i<5; i++) • numbers += " " + i; • System.out.println(numbers); // " 0 1 2 3 4"
String in Java (2) String s = "whatever", t = "whatnow"; s.charAt(0); // 'w' s.charAt(3); // 't' t.substring(4); // "now" (positions 4 through the end) t.substring(4,6); // "no" (positions 4 and 5, but not 6) s.substring(0,4); // "what" (positions 0 through 3) t.substring(0,4); // "what" s.compareTo(t); // a value less than zero; s precedes t in "lexicographic" // (dictionary) order t.compareTo(s); // a value greater than zero (t follows s) t.compareTo("whatnow"); // zero t.substring(0,4) == s.substring(0,4); // false (they are different String objects) t.substring(0,4).equals(s.substring(0,4));// true (they are both equal to "what") t.indexOf('w'); // 0 t.indexOf('t'); // 3 t.indexOf("now"); // 4 t.lastIndexOf('w'); // 6 t.endsWith("now"); // true //TestString.java
Binding in Java(1) public class Binding { public static void main(String[] arg) { Animal a=new Animal(); Dog b=new Dog(); b.speak(); //woof ! a = b; a.speak(); //woof ! Bird c=new Bird(); c.speak(); //tweet ! a = c; a.speak(); //tweet ! } }//binding.java class Animal { public void speak () { System.out.println("Animal Speak !\n"); } void reply () { System.out.println("Animal Reply !\n"); } }; class Dog extends Animal { public void speak () { System.out.println("woof!\n"); } void reply () { System.out.println("woof again!\n"); } }; class Bird extends Animal { public void speak () { System.out.println("tweet !"); } };
Binding in Java (2) public class windows { public static void main(String[] arg) { TextWindow x=new TextWindow(); Window a=new Window(); Window b; TextWindow c; a.oops(); a = x; a.oops(); b = x; b.oops(); c = x; c.oops(); } }//windows.java //Project: WindowCalssesForBinding class Window { public void oops() { System.out.println("Window oops\n"); }; private int height, width; }; class TextWindow extends Window { public void oops() { System.out.println("TextWindow oops\n"); }; private String contents; private int cursorLocation; };