CSCI 161 Final Exam Spring 2013

There will be a three hour final exam held in our regular classroom (bldg 315 rm 216) at 9am on Wednesday April 24th, worth 40% of your final grade.

The final exam will be closed book, closed notes, and no electronics permitted, but you will be allowed one double-sided 8.5x11 inch sheet of notes.

There will be 10 questions worth 9 marks each, but the exam mark will be recorded out of 80, not 90. (Marks greater than 80 will be recorded as 80.)

If a student missed one or both midterms their final exam mark will be substituted for the missing mark(s).

If a student's final exam mark is higher than their midterm mark(s) then their final exam mark will replace the midterm mark(s).


Topics

Pretty much anything we have covered in labs, lectures, or assignments could be exam material, but it will emphasize the core topics, particularly those that were not addressed in the midterms.


PRACTICE QUIZZES

Here are three practice quizzes from 2012:


PRACTICE EXERCISES

Here are some practice questions dug up from years past.

I haven't checked them out yet for possible errors, and some of them are a bit over-the-top for an exam question (for the actual exam I'll aim for eight questions that can be completed in 15-30 minutes each) but these should be good practice at least ;-)

They're roughly grouped by topic, but some overlap multiple topics.


    File I/O

  1. Suppose we have the following enumerated type to list possible File I/O errors:
    enum FileError {
       NoFileError,       // no error detected
       OpenInputError,    // error opening file for input
       OpenOutputError,   // error opening file for output
       UnexpectedFileEnd, // reached end-of-file at unexpected spot in program
       EmptyFileError,    // file contained no data
    };
    
    Provide appropriate implementations for the three following file handling routines:
    FileError OpenInputFile(ifstream& infile, char *filename);
    // open the named file for input, and return the opening status
    
    FileError OpenOutputFile(ofstream& outfile, char *filename);
    // open the named file for output, and return the opening status
    
    FileError CopyNChars(ifstream& infile, ofstream& outfile, int NumChars);
    // check to ensure the two files are open,
    //    then copy NumChars characters from the input file to the output file
    

  2. Write a function named CopyCapitals that takes two reference parameters, one of type ifstream and one of type ofstream, and returns an integer value.
    The functionality of CopyCapitals should be as follows:
    int CopyCapitals(ifstream& infile, ofstream& outfile)
    


    Command line arguments

  3. Write a complete C++ program that can accept command line arguments. The program should print the first and last characters of each command line argument, except for the name of the executable file itself.

  4. As you may have observed, we can supply any number of command line arguments as parameters to the "main" function.

    We can use a similar mechanism to pass any number of parameters to other functions. In the example below, the sumargs function takes a number as the first parameter, and this number indicates the number of other parameters that will follow. The other parameters are all strings in the code example, and are passed straight along from argv.

    You are to implement the bodies of both the main routine and the sumargs function, including appropriate error checking for the number of arguments.

    #include <cstdlib>
    #include <iostream>
    using namespace std;
    
    double sumargs(int num, char *params[])
    // num indicates the number of string parameters
    // params is the array of strings to be processed
    // 
    // the function returns the sum of the floating point equivalents
    //     of all the strings in params 
    // e.g.  sumargs(3, "17", "200", "0.3") would return 217.3
    //
    // use atof as the string-to-float conversion function
    {
    }
    
    int main(int argc, char *argv[])
    // take the sum of the values of all the command line arguments,
    // assuming the atof conventions are adequate for
    //    converting strings to numbers
    {
    }
    

  5. (i) Describe the effect of the following program and comment it appropriately.
    (ii) Provide more meaningful names for the error types, and more meaningful error messages.
    #include <fstream>
    #include <stdlib>
    using namespace std;
    
    enum ErrorTypes { err0, err1, err2, err3, err4, };
        
    ErrorTypes process(char *n1, char *n2)
    {
       ifstream f1; 
       ofstream f2;
    
       f1.open(n1);
       if (f1.fail()) {
          cout << "ERR3" << endl;
          return err3;
       }
    
       f2.open(n2);
       if (f2.fail()) {
          cout << "ERR4" << endl;
          f1.close();
          return err4;
       }
    
       while (!f1.eof()) 
          f2.put( static_cast<char>(f1.get()) );
    
       f1.close();
       f2.close();
    
       return err0;
    }
    
    int main(int argc, char *argv[])
    {
       ErrorTypes result;
    
       if (argc < 3) {
          cout << "ERR1" << endl;
          exit(err1);
    
       } else if ((argc % 2) == 0) {
          cout << "ERR2" << endl;
          exit(err2);
    
       } else {
         for (int i = argc-1; i > 1; i-=2) {
    
            result = process(argv[i], argv[i-1]);
    
            if (result != err0) exit(result);
         }
       }
       exit(err0);
    }
    


    Classes and inheritance

  6. For the class below, explain how the GetArray method could be used to violate the principles of an abstract data type. Give a code example which clearly supports your explanation.
    class IntegerArray {
    public:
       IntegerArray(int size = 50);             // create an array of the specified size
       ~IntegerArray();                                  // deallocate the array storage
       void SetElement(int index, float value); // set an element to the specified value
       float GetElement(int index);                      // return the specified element
       float *GetArray() { return Elements; }           // return a pointer to the array
    
    private:
       int ArraySize;       // the number of elements in the dynamically-allocated array
       float *Elements;       // a pointer for a dynamically-allocated array of elements
    };
    

  7. Suppose we have the following class derivations:
    class firstclass {
       private:
          int A1;
       public:
          int A2;
       protected:
          int A3;
    };
    
    class secondclass : private firstclass {
       private:
          int B1;
       public:
          int B2;
       protected:
          int B3;
    };
    
    class thirdclass : protected secondclass {
       private:
          int C1;
       public:
          int C2;
       protected:
          int C3;
    };
    
    class fourthclass : public thirdclass {
       private:
          int D1;
       public: 
          int D2;
          void changestuff();
       protected:
          int D3;
    };
    
    Assuming there are no other access methods provided, give a list of all the class variabes that method void fourthclass::changestuff() can access.

  8. Give a short example showing the use of multiple inheritance, including within it a name clash in the inherited data fields.

    Show how methods in the ultimate derived class can access the data fields in spite of the name clashes.


    Function, method, and operator overloading

  9. The following code segment overloads the () operator in the context of string handling. Determine what the purpose of the overloaded operator is, and show an example of its use.
    AStringClass  AStringClass::operator()(int a, int b)
    {
       AStringClass tmp(b - a + 1);
       for (int i = a; i <= b; i++) {
           tmp.s[i-a] = s[i];
       }
       tmp.s[i] = '\0';
       return tmp;
    }
    

  10. (i) Clearly identify the ambiguities which prevent the compiler from successfully compiling the following code segment:
    #include <iostream>
    using namespace std;
    
    int foo(int x = 0, char y = 'c');
    char foo(int x, char y); 
    char foo(char *x, char y);
    void foo(float x);
    void foo(float x, int y = 0);
    void foo(float x, char y);
    
    int main()
    {
       cout << foo(3, 'x');
       cout << foo("foo", 'x');
       cout << foo(3, 4);
       cout << foo(3);
       cout << foo('x');
       foo(3);
       foo(3, 4);
       foo(3, '4');
    }
    
    // Assume the function implementations follow below
    
    
    (ii) Provide an alternative set of methods which solve the problems, and explain your solution.


    Dynamic binding

  11. Consider the following code segment:
    #include <iostream>
    using namespace std;
    
    class Pet {
       public:
          Pet(char *n = NULL) {
             if (n == NULL) name = NULL;
             else { 
                name = new char[strlen(n)+1];
                strcpy(name, n);
             }
          }
          ~Pet() { delete name; }
          virtual void print() = 0;
       protected:
          char *name;
    };
    
    class Cat: public Pet {
       public:
          Cat(char *n = NULL, char *b = NULL) : Pet(n) {
             if (b == NULL) breed = NULL;
             else {
                breed = new char[strlen(b)+1];
                strcpy(breed, b);
             }
          }
          void print() { 
             cout << name << " the cat is a " << breed << endl; 
          }
       private:
          char *breed;
    };
    
    class Dog: public Pet {
       public:
          Dog(char *n = NULL, char *b = NULL) : Pet(n) {
             if (b == NULL) breed = NULL;
             else {
                breed = new char[strlen(b)+1];
                strcpy(breed, b);
             }
          }
          void print() { 
             cout << name << " the dog is a " << breed << endl; 
          }
       private:
          char *breed;
    };
    
    
    Write a main routine and function call which clearly show polymorphism in action, and explain the sequence of events (with respect to polymorphism) that take place during the relevant function and method calls.


    Linked lists

  12. Suppose we have the following class definition for a doubly-linked list of integers:
    class list {
       public:
          list();              // construct an empty list
          list(const list& L); // copy constructor
          ~list();             // deallocate all list nodes
    
          // create and insert a node at the front of the list
          bool insertFront(int d); 
    
          // remove and deallocate all nodes containing data value d,
          //    returning a count of how many were removed
          int deleteValues(int d);
    
       private:
          // specs for individual nodes within the list
          struct listnode {
             listnode *next;
             listnode *prev;
             int      data;
          };
    
          // track the front element in the list
          listnode *front;
    
          // dynamically allocate and initialize a node
          listnode* createNode(int d); 
    };
    

  13. Write the body of the copy constructor, ensuring it is appropriate for the dynamically-allocated nature of the list contents.

  14. Write the body of the deleteValues method.

  15. Write the body of the insertFront method, assuming the createNode method has already been implemented (i.e. using it to allocate the new node).


    Stacks and queues

  16. Suppose we have the following class:
    class TheClass {
    public:
       TheClass(int size); // creates a new (empty) TheClass which can hold
                           //    the specified number of data elements
       ~TheClass();             // deletes TheClass storage space
       void Insert(float data); // inserts a new data element in TheClass
       float Remove();     // removes and returns a data element from TheClass
    
    private:
       float *DataList; // dynamically allocated array for holding TheClass data
       int ArraySize;   // size of the dynamically-allocated array
       int CurrentSize; // number of data elements currently in the array
       int Index;       // can be used, if you need it, to track some other
                        //     position within the array
    };
    
    Provide an implementation of the Remove method that would be valid if TheClass represented a stack.

  17. Assume we have the following descriptions of a stack class and a queue class.
    class stack {
       public:
          stack(int size = MAXSIZE);
          ~stack();
          void push(char c);
          char pop();
          bool isempty();
       private:
          // hidden stuff
    };
    
    class queue {
       public:
          queue(int size = MAXSIZE);
          ~queue();
          void enqueue(char c);
          char dequeue();
          bool isempty();
       private:
          // hidden stuff
    };
    
    A palindrome is a word that is spelled the same backwards as forwards, e.g. "bob", "a", "adbda", etc. You are to implement a palindrome-checking function that operates precisely as follows: You may assume that the default stack and queue size are sufficiently large to handle the user's input. The function header is supplied below.
    bool IsPalindrome() {
    

  18. Suppose we are relying on a stack class with the following supported methods:
    stack::stack();
    stack::~stack();
    Boolean stack::isempty();
    Boolean stack::isfull();
    datatype stack::top();
    void stack::pop();
    void stack::push(datatype d);
    
    (You may assume that datatype and Boolean are data types defined in the stack header file.)

    Write a function named printzero that takes a stack as a reference parameter, and removes all data items from the stack, printing out every element in the stack that does not have value defaultvalue, where defaultvalue is also declared in the stack header file.

    void printzero(stack& s)
    


    Trees and search trees

  19. Suppose we build a binary search tree of integers, inserting the data values 3, 4, 5, 1, 3, 2, 4, 6, 2 in that order.

    Sketch the tree which results.

  20. For the binary tree shown below, give the results of a postorder traversal of the tree
                  9
                 / \
                /   \
               1     7
              / \
             2   3
                  \
                   5
                  /
                 1
    

  21. Give pseudo-code for a recursive routine to delete a node from a binary search tree, and in your own words describe how and why the routine works.

  22. Suppose you had to write the contents of a binary search tree into a file, and later read the data back in to re-create the same tree structure. What would be an appropriate order (inorder, preorder, or postorder) to write the data values in, and why?


    Sorting and searching algorithms

  23. For the following recursive algorithm, assuming the array is sorted and contains N data values, describe in your own words why the search time is considered to be proportional to log2(N).
    // return the position of the key value in the array segment,
    //    or -1 if the value is not present
    int search(int arr[], int lower, int upper, int key)
    {
       if (key < arr[lower]) return -1;
       if (key > arr[upper]) return -1;
       if (lower > upper) return -1;
       if (lower < 0) return -1;
       int midpoint = (lower + upper) / 2;
       if (key == arr[midpoint]) return midpoint;
       if (key < arr[midpoint])
          return search(arr, lower, midpoint-1, key);
       return search(arr, midpoint+1, upper, key);
    }
    

  24. For the quicksort and partition routines shown below:
    int partition(int arr[], int first, int last)
    {
       int lower, upper;
       int pivotvalue = arr[first];
    
       lower = first;
       upper = last;
    
       do {
          while ((pivotvalue  >= arr[lower]) && (lower <= last)) lower++;
          while (pivotvalue < arr[upper]) upper--;
          if (lower < upper) swap(arr[lower], arr[upper]);
       } while (lower < upper);
       
       swap(arr[first], arr[upper]);
       return upper;
    }
    
    void quicksort(int arr[], int first, int last)
    {
       if ((first >= last) || (first < 0)) return;
       int pivot = partition(arr, first, last);
       quicksort(arr, first, pivot-1);
       quicksort(arr, pivot, last);
    }
    


    Exception handling

  25. The use of try, throw, and catch provides good support for exception handling, but adds considerable overhead and can, if used unwisely, lead to a loss of clarity in the code. Briefly discuss (1-2 paragraphs) the kinds of situations in which such exception handling would be suitable, and provide some justification for your answer.

  26. Show the output from the following program
    #include <iostream>
    using namespace std;
    
    void f1(double t);
    int main()
    {
       try {
         cout << "stage 1" << endl;
         f1(80);
         cout << "stage 2" << endl;
         f1(40);
         cout << "stage 3" << endl;
       }
       catch (int x) {
          cout << x << endl;
       }
       cout << "stage 4" << endl;
    }
    void f1(double t)
    {
       cout << "Beginning f1" << endl;
       if (t < 50) throw 25;
    }
    

  27. Write a C++ function that incorporates exception handling (using try/throw/catch) with the following interface and black box description:
    void printsquares(char *data[], int numvalues)
    // printsquares takes as parameters an array of null-terminated strings
    //    and an integer describing the number of strings in the array
    // (an error message is generated if there is not at least one string)
    //
    // printsquares evaluates each string to see if it is a positive integer,
    //    and if so prints the square of that integer
    //    (if not then an error message is generated)
    
    You may use existing library routines, but include a comment indicating which C++ library is required.

  28. The Java programming language is in many ways similar to C++.
    However, the developers of the Java programming language decided not to include multiple inheritance as an option for classes in that language.
    Describe possible advantages and disadvantages associated with such a decision.


    Templates and the STL

  29. The program below uses the C++ standard library classes for strings and templated stacks. Deduce what the program does, and comment the code appropriately.
    #include <iostream>
    #include <string>
    #include <stack>
    using namespace std;
    
    const int MAXSIZE = 256;
    
    int main(int argc, char *argv[])
    {
       stack<string> MyStack;
    
       string *s;
     
       string t;
     
       for (int i = 1; i < argc; i++) {
     
          s = new string(argv[i]);
     
          MyStack.push(*s);
     
       }
       
       cout << MyStack.size() << endl;
       
       while (!MyStack.empty()) {
     
          t = MyStack.top();
     
          MyStack.pop();
     
          cout << t << endl;
     
       }
       
    }
    


    Testing

  30. The following routine is supposed to display what time it is in a different time zone. The first two parameters are the time (hours and minutes, respectively) in zone 1, while the third parameter is the amount (in hours) that time must be adjusted by to determine the time in zone 2.

    The hours for time zone 1 can be 0..23, while the minutes can be 0..59. The adjustment value is a float (since some time zones differ by fractions of an hour) and can be positive or negative.

    If valid parameters are supplied then the time in zone 2 is computed and displayed, otherwise an appropriate error message is displayed.

    The header for the function is:

    void timeCalc(int hours1, int minutes1, float diff);           
    
    Write a set of test values you would apply to determine if the function was working correctly. For each test set, give a value for hours1, minutes1, diff, and the expected result, plus a short description of why that particular test set has been included