Practice Problems CS2620 Advanced Programming, Spring 2003 April 9, 2003 1. Explain the results of each vector definition: string pals[] = "pooh", "tigger", "piglet", "eeyore", "kanga" a) vector<string> svec1(pals, pals+5); Solution: svec1 is a vector of string that has been initialized with the content of the array of string pals. b) vector<int> ivec1(10); Solution: ivec1 is a vector of 10 ints. c) vector<int> ivec2(10, 10); Solution: ivec2 is a vector of 10 ints, each initialized with the value 10 d) vector<string> svec2(svec1); Solution: svec2 is a vector of string that is a copy of the vector svec1 2. A type/subtype inheritance relationship, in general, reflects an Is-A relationship e.g., a book is a kind of librarymaterial. Which of the following pairs reflects an Is-A relationship and which one does not? Explain. (a) Member function is-a kind of function Solution: This reflects an Is-A relationship. A member function is a specialized instance of function. Both have a return type, a name, a parameter list, and a definition. In addition, a member function belongs to a particular class, may or may not be a virtual, const, or static member function, and so on. Inheritance correctly models this relationship. (b) Journal Is-A kind of library Solution: This does not reflects an Is-A relationship. A journal is an item that a library may contain. A journal is not a specialize instance of library. The relationship is an example of a Has-A relationship. 1
3. The following function provides absolutely no checking of the possible failure of an operation or the validity of data. int *allocateandinit( const string& filename) ifstream infile(filename.c_str()); int elementcount; infile >> elementcount; // read the number of ints contained in // the file // allocatearray(elementcount) allocates elementcount integers from the // free store and returns a pointer to the beginning of the allocated storage int *pi = allocatearray(elementcount); int elt; int index = 0; while (cin >> elt) // read data pi[ index++ ] = elt; // sortarray(pi, elementcount) sorts an array of // elementcount ints sortarray(pi, elementcount); return pi; (a) Write the prototype declarations for the functions allocatearray() and sortarray() from the information given above. Solution: Prototype declarations: int *allocate_array(int); void sortarray(int *, int); (b) Identify what might possibly go wrong within the function. Solution: The following might possibly go wrong within the function filename could be an empty string The ifstream constructor may not be able to open the file even if filename is a valid string elementcount receives an incorrect value: too large, zero, or a negative value allocatearray() fails to allocate space for elementcount elements The while loop depends on encountering EOF (or failing to read an int) to terminate the loop, and it does not take into account the size of the array that pi points to. 4. Rewrite the following class definition to make it a class template: class C 2
C(); // default constructor C(int *par, int sz); // construct from an int array of sz elements // the data members minm and maxm are computed // from the array par int& operator[]( int index); //overloaded operator []: return element at //position index bool operator==(const C&) const; // Overloaded operator == : return true // on equality, and false otherwise bool insert(int elt); // insert elt at the back int min() const return minm; // Return the minimum element int max() const return maxm; // return the maximum element int size; // Number of elements in the array int* parray; // Actual array int minm; // minimum value of the array elements int maxm; // maximum value of the array elements void min(double); // compute the minimum void max(double); // compute the maximum Solution: To transform the above class definition into a template, we must identify and factor out each dependent data type. size, for example, is of type int. Might that vary with different user-specified instances of C? Not really. size is an invariant data member that holds a count of the elements addressed by parray. parray, however, may address elements of varying types: int, double, float, string, etc. We want to parameterize the data types of members parray, minm, maxm, as well as return type and signature of the appropriate member functions. template <class T> class C C(); // default constructor C(const T *par, int sz); // construct from an int array of sz elements // the data members minm and maxm are computed // from the array par T& operator[]( int index); //overloaded operator []: return element at //position index bool operator==(const C&) const; // Overloaded operator == : return true // on equality, and false otherwise bool insert(const T& elt); // insert elt at the back T min() const return minm; // Return the minimum element 3
T max() const return maxm; // return the maximum element T size; // Number of elements in the array T* parray; // Actual array T minm; // minimum value of the array elements T maxm; // maximum value of the array elements 5. We can make the Bubble sort implementation (refer to lecture notes) slightly efficient by terminating the outer for loop early, i.e., as soon as we detect that the remaining elements are sorted. Implement the early termination version of bubble sort algorithm. (Note: Although this modification can make the algorithm run faster for nearly sorted data, the worst-case time complexity does not change). Solution: If no swapping is performed in an outer for loop iteration (bubbling phase), the elements are sorted. On the other hand, a single swap during the bubbling phase implies unsorted data. We can explicitly test for this condition and terminate the loop when the condition is true. template<class T> void bubblesort(vector<t>& elts) bool sorted = false; //assume the list is unsorted int numelts = elts.size(); int pass = 0; while ((pass < numelts-1) &&!sorted) // the while loop // replaces the for loop sorted = true; //may be that the remaining elements are sorted for (int j = 0; j < numelts-pass-1; j++) if (elts[j] > elts[j+1]) swap(&elts[j], &elts[j+1]); sorted = false; // a swap implies unsorted data //end_if // end_for pass++; //end_while //end_bubblesort 6. Explain why the stream extraction and insertion operators are typically implemented as nonmember functions for user-defined classes. Solution: The input and output operators are not defined global because the left operand of each operator is a stream instead of a user-defined class object. An operator member function will be called only if the left operand is an object of the class type for which the operator is being overloaded. 7. The conversion constructors can be a potential source of hard-to-find bugs. Explain what might go wrong. 4
Solution: Refer to Lecture Note. 8. Consider the following Clock class. // A simple Clock class interface specification. Keeps time in Hour, Minute, // and Second // File: clock.h // Date: 2000/11/03 #ifndef CLOCK_H #define CLOCK_H //Prevent multiple inclusions class Clock // Member functions // Default constructor with the default value of // Midnight: 0 hour, 0 minute and, 0 second Clock(int hr=0, int mnt=0, int sec=0); // Inspectors (get methods) // Declared const as they do not modify the invoking // object s state; also necessary to be able to process // const objects. These functions are ideal candidate for // inline specification! // If the function body is included, the functions // becomes inline automatically -- i.e., explicit // inline keyword is not necessary int gethour() const; // Return Hour int getminute() const; // Return Minute int getsecond() const; // Return Second // Mutators (set methods) // setclock() will set an existing clock with the supplied // values: hr,mnt,sec where 0<= hr <= 23, 0<= mnt <= 59, // and 0 <= sec <= 59 // If the supplied values are illegal then nothing is changed // // Note reference return to the object itself as we need to make // cascaded calls. Clock& setclock(int hr, int mnt, int sec); // Perform one clock tick() -- advance the clock by one second. // A reference return will enable cascaded call: obj.tick().tick(). 5
Clock& tick(); // Utility functions // Print the current value of clock void display() const; protected: // Data members int Hour; // Hour value: 0 <= Hour <= 23 int Minute;// Minute value: 0 <= Minute <= 59 int Second; // Second Value: 0 <= Second <= 59 // Helpers // Check data for validity bool validtime(int hr, int mnt, int sec) const; // Internal method to set time void settime(int hr, int mint, int sec); #endif An alarm clock is a clock with the following additional functionalities. An alarm can be set at a particular time, and can be deactivated in which case the alarm will not ring at the set time. Define and implement the class alarmclock with software reuse in mind. You may assume that the clock class has already been implemented and available to you. Solution: Try Yourself. 9. Which class definition is likely to need a copy constructor? Explain. (a) A complex number representation containing two floating point numbers. Solution: An explicit copy constructor is not needed since there are no data members that are pointers. (b) A word class containing a string object and vector object of line and column location pairs. Solution: A copy constructor is not required because the compiler will invoke the copy constructors for the data members of type string and vector 10. Determine the output of the following code. #include <iostream> 6
#include <string> using namespace std; class bclass bclass(string data = "bclassdata"):bs(data) virtual void Display() cout << "In bclass::display() "; cout << "bs = " << bs << endl; void Print() cout << "In bclass::print() "; cout << "bs = " << bs << endl; string bs; class dclassone : public bclass dclassone(string data = "dclassonedata"):d1s(data) virtual void Display() cout << " In dclassone::display()"; bclass::display(); cout << " d1s = " << d1s << endl; void Print() cout << " In dclassone::print()"; bclass::print(); cout << " d1s = " << d1s << endl; string d1s; class dclasstwo : public bclass dclasstwo(string ddata = " dclasstwodata ", string bdata= " bclassdatafromd2 ") :bclass(bdata), d2s(ddata) 7
virtual void Display(int i) cout << " In dclasstwo::display()"; bclass::display(); cout << " d2s = " << d2s << " i = " << i << string d2s; endl; void Output( bclass &R) R.Display(); int main() bclass A("Object A"); dclassone B("Object B"); dclasstwo C("Object C", "Object C ( ObjectC_A ) "); bclass *Ptr; Ptr = &B; Ptr->Display(); Output(A); Output(B); A = B; A.Display(); Ptr = &B; Ptr->Print(); C.Display(3); Ptr = &C; Ptr->Display(); return 0; 8