CISC 2000 Computer Science II Fall, 2014 Note 12/1/2014 1 Review of Inheritance Practice: Please write down 10 most important facts you know about inheritance... (a) What s the purpose of inheritance? Can you list some example where inheritance can save works? (b) What is base class (parent class), and what is derived class (child class)? (c) How to do it (in C++)? (d) What are inherited? How to access inherited members? (e) What s the meaning of protected keyword? (f) How to redefine an inherited member function in the derived class? (g) How to call base class s version of the redefined function?
(h) How does compiler figure out which version of the redefined function should be called (the base class s or the derived class s)? 2 Function overloading, function redefinition. Functions are not uniquely identified by their names, as you can define multiple functions of the same name, as long as their signatures are different. Two overloaded functions must not have the same signature. In the textbook: A function s signature is the function s name, with the sequence of types in the parameter list, not including the const keyword and not inlcudeing the ampersand, nor the return type. In reality, g++ allows the following (i.e., it includes const, and ampsand in function signature). #include <iostream> using namespace std; void funca(int a); void funca(int & a); //overload funca int main() int a=10; funca(10); //the first funca() will be called, as 10 cannot match int & //funca (a); this will give a compilation error, as there will be ambiguity about which // funca should be called... void funca(int a) a=10; void funca(const int & a) int b=a; return a; 2
Common Pitfalls When writing function headers If a function has the same name in a derived class as in the base class but has a different signature, that is overloading, not redefinition. class Figure... virtul void Display (bool Erase=true)=0; //=0 means this class (Figure) does not know how // to display, as it s an abstract class... ; class Rectangle... void Display (const bool & erase) //This does not redefine Display() as declared in // Figure class (as the signature of this function does // not match the one in Figure). // So Rectangle is still an abstract class, as there is still // some undefined virtual function...... ; When listing a function as a friend function of a class, the signature (header) in the function s definition should match with that listed in class headers; otherwise, compiler thinks you have two functions. class rational public: friend rational operator+ (const rational & a, const rational & b); //this is one function... rational operator+ (rational & a, rational &b) //this is a different function, as the two signatures are different! //g++ consider const to be part of the signature.. rational tmp; tmp.numerator =... // compliation error! 3 Polymorphism and virtual function (a) Is-a relationship: In object-oriented programming, an object of the derived class may be referred to via a pointer or reference of the base class type instead of the derived class type, Figure * curfig;... curfig = new Rectangle (...); // where Rectangle is derived from Figure... 3
(b) Early-binding (default) and Late-binding (virtual function): If there are base class methods redefined the derived class, the method actually called via such a reference or pointer can be bound either early (by the compiler), according to the declared type of the pointer or reference, or late (i.e. at runtime), according to the actual type of the object. Virtual functions are resolved late. If the function in question is virtual in the base class, the most-derived class s implementation of the function is called according to the actual type of the object, regardless of the declared type of the pointer or reference. If it is not virtual, the method is resolved early and the function called is selected according to the declared type of the pointer or reference. (c) Pure virtual function: The base class only provides the interface for such function, and leaves the implementation to the derived classes, by adding = 0 to the end of function declaration. class Figure void Move (int dx, int dy) //this move function works for all derived class object Display (true); x = x + dx; y = y + dy; Display (false); virtual Display (bool erase)=0; //We don t know how to display an abstract Figure object... // can only implement this in Rectangle, Triangle,... classes private: int x; int y;.. ; A class with at least one pure virtual function is called abstract class. One cannot instantiate an object of an abstract class type: Figure figurelist[20]; // compiler error! Figure is abstract class... Rectangle reclist[20]; //20 Rectangle objects... Figure * figlist[20]; // ok... array of pointers to Figure, // can store address of any object of Figure-derived type, // such as Rectangle, Triangle,... 4 How to read variable declaration statements? C++ variable declarations can be extremely complex. There is a specific process to follow in order to read them correctly. In general, you will follow an inside-out procedure. (a) Start at the variable name. (b) Read the first item to the right of the variable name (if there is one). parenthesis, go to the next step. If the item is a right (c) Read the first item to the left of the variable name (if there is one). If the item is a left parenthesis, go to the next step. 4
(d) Read the next item to the right of the variable name (if there is one). If the item is a right parenthesis, go to the next step. (e) Read the next item to the left of the variable name (if there is one). If the item is a left parenthesis, go to the next step. (f) Repeat Steps (d) and (e) until you run out of items to read. Usually you will run out of items to the right of the variable name well before you run out of items to the left. The items you will most frequently encounter in a C++ variable declaration are summarized in the following table: Item Appears Read as int, Date,... to the left of the variable name exactly as it appears const to the left of the variable name constant * to the left of the variable name pointer to & to the left of the variable name reference to ( to the left of the variable name not read, skip to next step [] To the right of the variable name array of [6] To the right of the variable name array of 6 [][10] To the right of the variable name two-dimensional array of an unknown number of rows with 10 columns of [4][10] To the right of the variable name two-dimensional array of 4 rows with 10 columns of ) to the right of the variable name not read, skip to next step Please practice the above guidelines by reading out (writing out) the following variable declaration, and then illustrating the variable by drawing out its memory map: (1)int a[20]; (2) const int LABNUM=4; int * labs[labnum]; (3) char board[3][3]; (4) Date listofdates1[10]; (5) Date * listofdates2; (6) Date * listofdates3[10]; 5
(7) Date DaysLater (const Data & date, int dayspassed); //What s the parameters type? (8) void PrintBoard (int board[][4], int row); //what s the first parameter s type? (9) // if you are building a game program that allows the user to input the size of // the game board at running time... int row, col; cout << "How large is the board? enter the number of rows, number of cols:"; cin >> row >> col; // How do you declare the variables, and allocate the memory? // Recall "lab monitor" lab? (10) int *p; int * & b = p; (11) int * * p[4]; 5 C++ reference dymystified A reference variable is an alias, that is, another name for an already existing variable. Once a reference is initialized with a variable, either the variable name or the reference name may be used to refer to the variable. (a) Basics of references Think of a variable name as a label attached to the variable s location in memory. You can then think of a reference as a second label attached to 6
that memory location. Therefore, you can access the contents of the variable through either the original variable name or the reference. difference between C++ References vs Pointers: You cannot have NULL references. You must always be able to assume that a reference is connected to a legitimate piece of storage. Once a reference is initialized to an object, it cannot be changed to refer to another object. Pointers can be pointed to another object at any time. A reference must be initialized when it is created. Pointers can be initialized at any time. int main () // declare simple variables int i=1,j=2; double d; // declare reference variables int& r = i; // r is reference variable for i // read "r is an integer reference initialized to i" double & s = d; // read "s is a double reference initialized to d." // int & r1; this won t be allowed, as we need to initialize reference when declaring it // int & refa = 20; compilation error: non-const lvalue reference to type int // cannot bind to a temporary of type int // int & refb = i+2; compilation error for similar reason to the above i = 5; cout << "Value of i : " << i << endl; cout << "Value of i reference : " << r << endl; cout <<" size of i reference: " << sizeof(r) <<endl; r = j; // this is allowed, what s the effect? r=20; cout <<"Value of i : " << i << endl; cout << "Value of i reference : " << r d = 11.7; cout << "Value of d : " << d << endl; cout << "Value of d reference : " << s << endl; << endl; return 0; (b) Pass-By-Reference Parameters 7
Now, let s take another look at how pass-by-reference paramter works, using the following code sample: Can you write a simple sentnce about how pass-by-reference parameter work after the discussion? #include <iostream> using namespace std; // function declaration void swap(int& x, int& y); int main () // local variable declaration: int a = 100; int b = 200; cout << "Before swap, value of a :" << a << endl; cout << "Before swap, value of b :" << b << endl; /* calling a function to swap the values.*/ swap(a, b); cout << "After swap, value of a :" << a << endl; cout << "After swap, value of b :" << b << endl; return 0; // function definition to swap the values. void swap(int& x, int& y) int temp; temp = x; /* save the value at address x */ x = y; /* put y into x */ y = temp; /* put x into y */ return; Const Reference Parameter: Pass-by-value mechanisms often imply an expensive copy operation for large parameters. References qualified with const are a useful way of passing large objects between functions that avoids this overhead: void f_slow(bigobject x) /*... */ void f_fast(const BigObject& x) /*... */ 8
BigObject y; f_slow(y); // slow, copies y to parameter x f_fast(y); // fast, gives direct read-only access to y (c) Function returning reference #include <iostream> #include <ctime> using namespace std; double vals[] = 11.1, 22.2, 33.3, 44.4, 55.5; double& ReadWriteAccess( int i ) return vals[i]; // return a reference to the ith element of array vals //Note the usage of const modifier here: this indicates that //the reference to the variable is read-only: caller can not assigned value to the returned ref const double & ReadOnlyAccess (int i) if (i<0 i>5) cout <<"ReadOnlyAccess: index out of bounds\n"; exit (1); return vals[i]; // main function to call above defined function. int main () cout << "Value before change" << endl; for ( int i = 0; i < 5; i++ ) cout << "vals[" << i << "] = "; cout << vals[i] << endl; ReadWriteAccess(1) = 20.23; // assign 20.23 to the 2nd element (returned by ReadWRiteAccess) // ReadOnlyAccess(3) = 70.8; compilation error: read-only variable is not assignable //Can also save the reference returned by function to a reference variable int & a = ReadWriteAccess(2); //a is reference to the 3rd element of array vals a = 200; //assign 200 to the variable that a references to, i.e., vals[2]=200; const int & a_ref = ReadOnlyAccess (2); // a_ref is a read-only reference // to 3rd element of vals 9
// a_ref = 200; compilation error, as a_ref is read-only cout << a_ref <<endl; // display vals[2] s value (read access is allowed...) int b = GetRefToValues(0); // b is a int variable, assigned with values // stored in val[0] b = 1000; // val[0] is not changed... cout << "Value after change" << endl; for ( int i = 0; i < 5; i++ ) cout << "vals[" << i << "] = "; cout << vals[i] << endl; return 0; Common errors in returning reference int& Counter() int q; //return q; // Compile time error (q will go out of scope when function returns, so the refer // to q cannot be returned... static int x; x++; return x; //variable x is local variable (has a local scope), but has a static lifetime // Safe, x lives outside the lifetime of this function call // return x+1; compile error: x+1 is a temporary (does not have memory location)... // return 0; similar to the above error //Alternatively, you can also dynamically allcoated an object/variable, and return it as a re 6 lvalues versus rvalues: a simple definition (a) An lvalue (locator value) represents an object/variable that occupies some identifiable location in memory (i.e. has an address). (b) An rvalue is an expression that does not represent an object/variable occupying some identifiable location in memory. int var; // An assignment expects an lvalue as its left operand, and var is an lvalue, // because it is an object with an identifiable memory location. var = 4; 10
// On the other hand, the following are invalid: 4 = var; // ERROR! (var + 1) = 4; // ERROR! // Neither the constant 4, nor the expression var + 1 are lvalues (which makes them rvalues), // because both are temporary results of expressions, which don t have an identifiable memory // Therefore, assigning to them makes no semantic sense - there s nowhere to assign to. Conversions between lvalues and rvalues Generally speaking, language constructs operating on object values require rvalues as arguments. For example, the binary addition operator + takes two rvalues as arguments and returns an rvalue: int a = 1; // a is an lvalue, "value of 1 is assigned to variable a" int b = a; // b is an lvalue, "Variable a s VALUE is assigned to variable b" // an implicit lvalue-to-rvalue conversion occurs on a int c = a + b; // + needs rvalues, so a and b are converted to rvalues // and an rvalue is returned // a and b are both lvalues, in the third line, they undergo an implicit lvalue-to-rvalue conversio // "Variable a s VALUE and variable b s VALUE are added, and the result is assigned to variable c." 11