<<

Properties of an identifier (and the Example: object it represents) may be set at In • Compile-time These are static properties as • The of an identifier is the whole program or subprogram. they do not change during execution. Examples include • Each identifier may be declared only the type of a variable, the value once. of a constant, the initial value • Variable declarations may be implicit. of a variable, or the body of a (Using an identifier implicitly declares function. it as a variable.) • The lifetime of data objects is the • Run-time whole program. These are dynamic properties. Examples include the value of a variable, the lifetime of a heap object, the value of a function’s parameter, the number of times a while loop iterates, etc.

© © CS 538 Spring 2008 44 CS 538 Spring 2008 45

Block Structured Languages Binding of an identifier to its corresponding declaration is • Include Algol 60, Pascal, and Java. usually static (also called • Identifiers may have a non-global lexical), though dynamic scope. Declarations may be local to a binding is also possible. class, subprogram or block. Static binding is done prior to • Scopes may nest, with declarations execution—at compile-time. propagating to inner (contained) scopes. Example (drawn from C): • The lexically nearest declaration of an identifier is bound to uses of that int x,z; identifier. void A() { float x,y; print(x,y,z); int } float void B() { float print (x,y,z) int } undeclared int

© © CS 538 Spring 2008 46 CS 538 Spring 2008 47 Block Structure Concepts Variations in these rules of name scoping are possible. • Nested Visibility For example, in Java, the No access to identifiers outside lifetime of all class objects is their scope. from the time of their creation • Nearest Declaration Applies (via new) to the last visible Static name scoping. reference to them. • Automatic Allocation and Deallocation Thus of Locals ... Object O;... Lifetime of data objects is creates an object reference but bound to the scope of the does not allocate any memory Identifiers that denote them. space for O. You need ... Object O = new Object(); ... to actually create memory space for O.

© © CS 538 Spring 2008 48 CS 538 Spring 2008 49

Dynamic Scoping Example: An alternative to static scoping int x; is dynamic scoping, which was void print() { used in early Lisp dialects (but write(x); } not in Scheme, which is main () { statically scoped). bool x; Under dynamic scoping, print(); identifiers are bound to the } dynamically closest declaration Under static scoping the x of the identifier. Thus if an written in print is the lexically identifier is not locally closest declaration of x, which declared, the call chain is as an int. (sequence of callers) is examined to find a matching Under dynamic scoping, since declaration. print has no local declaration of x, print’s caller is examined. Since main calls print, and it has a declaration of x as a bool, that declaration is used.

© © CS 538 Spring 2008 50 CS 538 Spring 2008 51 Dynamic scoping makes type Virtual Functions checking and variable access harder and more costly than A function declared in a class, static scoping. (Why?) C, may be redeclared in a class derived from C. Moreover, for However, dynamic scoping uniformity of redeclaration, it is does allow a notion of an important that all calls, “extended scope” in which including those in methods declarations extend to within C, use the new subprograms called within that declaration. scope. Example: Though dynamic scoping may class C { seen a bit bizarre, it is closely void DoIt()(PrintIt();} related to virtual functions void PrintIt() used in C++ and Java. {println(“C rules!”);} } class D extends C { void PrintIt() {println(“D rules!”);} void TestIt() {DoIt();} } D dvar = new D(); dvar.TestIt(); D rules! is printed.

© © CS 538 Spring 2008 52 CS 538 Spring 2008 53

Scope vs. Lifetime In C: void p() { It is usually required that the static int i = 0; lifetime of a run-time object at print(i++); least cover the scope of the } identifier. That is, whenever Each call to p prints a different you can access an identifier, the value of i (0, 1, ...) Variable i run-time object it denotes retains its value across calls. better exist. Some languages allow an But, explicit binding of an identifier it is possible to have a run-time for a fixed scope: object’s lifetime exceed the Let { scope of its identifier. An id = val type id = val; example of this is static or own in statements variables. statements } end; A declaration may appear wherever a statement or expression is allowed. Limited scopes enhance readability.

© © CS 538 Spring 2008 54 CS 538 Spring 2008 55 Structs vs. Blocks Blocks and structs look similar, but there are significant Many programming languages, differences: including C, C++, C#, Pascal Structs are data, and Ada, have a notion of grouping data together into • As originally designed, structs structs or records. contain only data (no functions or methods). For example: • Structs can be dynamically created, struct complex { float re, im; } in any number, and included in There is also the notion of other data structures (e.g., in an array of structs). grouping statements and declarations into blocks: • All fields in a struct are visible outside the struct. { float re, im; re = 0.0; im = 1.0; }

© © CS 538 Spring 2008 56 CS 538 Spring 2008 57

Blocks are code, Classes • They can contain both code and data. • Class objects can be created as needed, in any number, and included • Blocks can’t be dynamically created in other data structure. during execution; they are “built into” a program. • They include both data (fields) and functions (methods). • Locals in a block aren’t visible outside the block. • They include mechanisms to initialize themselves (constructors) and to By adding functions and finalize themselves (destructors). initialization code to structs, • They allow controlled access to we get classes—a nice blend of members (private and public structs and blocks. declarations). For example: class complex{ float re, im; complex (float v1, float v2){ re = v1; im = v2; } }

© © CS 538 Spring 2008 58 CS 538 Spring 2008 59 Type Equivalence in Classes Then we may not want assignment to be allowed. In C, C++ and Java, instances of the same struct or class are class Point { type-equivalent, and mutually int dimensions; assignable. float coordinates[]; Point () { For example: dimensions = 2; class MyClass { ... } coordinates = new float[2]; MyClass v1, v2; } Point (int d) { v1 = v2; // Assignment is OK dimensions = d; coordinates = new float[d]; } } We expect to be able to assign Point plane = new Point(); values of the same type, Point solid = new Point(3); including class objects. plane = solid; //OK in Java However, sometimes a class models a data object whose This assignment is allowed, size or shape is set upon even though the two objects creation (in a constructor). represent points in different dimensions.

© © CS 538 Spring 2008 60 CS 538 Spring 2008 61

Subtypes Parametric Polymorphism In C++, C# and Java we can We can create distinct create subclasses—new classes subclasses based on the values derived from an existing class. passed to constructors. But We can use subclasses to create sometimes we want to create new data objects that are subclasses based on distinct similar (since they are based on types, and types can’t be a common parent), but still passed as parameters. (Types type-inequivalent. are not values, but rather a property of values.) Example: We see this problem in Java, class Point2 extends Point { which tries to create general Point2() {super(2); } purpose data structures by } basing them on the class class Point3 extends Point { Object. Since any object can be Point3() {super(3); } Object } assigned to (all classes Point2 plane = new Point2(); must be a subclass of Object), Point3 solid = new Point3(); this works—at least partially. plane = solid; //Illegal in Java

© © CS 538 Spring 2008 62 CS 538 Spring 2008 63 class LinkedList { • We must use wrapper classes like Object value; Integer rather than int (because LinkedList next; primitive types like int aren’t Object head() {return value;} objects, and aren’t subclass of LinkedList tail(){return next;} Object). LinkedList(Object O) { value = O; next = null;} For example, to use LinkedList LinkedList(Object O, to build a linked list of ints we LinkedList L){ do the following: value = O; next = L;} } LinkedList l = new LinkedList(new Integer(123)); Using this class, we can create int i = a linked list of any subtype of ((Integer) l.head()).intValue(); Object. This is pretty clumsy code. But, We’d prefer a mechanism that allows us to create a “custom • We can’t guarantee that linked lists are type homogeneous (contain version” of LinkedList, based only a single type). on the type we want the list to contain. • We must cast Object types back into their “real” types when we extract list values.

© © CS 538 Spring 2008 64 CS 538 Spring 2008 65

We can’t just call something like Thus we have LinkedList(int) or class LinkedList { LinkedList(Integer) because T value; LinkedList next; types can’t be passed as T head() {return value;} LinkedList tail() { parameters. return next;} LinkedList(T O) { Parametric polymorphism is value = O; next = null;} the solution. Using this LinkedList(T O,LinkedList L) mechanism, we can use type {value = O; next = L;} } parameters to build a “custom LinkedList l = version” of a class from a new LinkedList(123); general purpose class. int i = l.head(); C++ allows this using its template mechanism. Tiger Java also allows type parameters. In both languages, type parameters are enclosed in “angle brackets” (e.g., LinkedList passes T, a type, to the LinkedList class).

© © CS 538 Spring 2008 66 CS 538 Spring 2008 67