CS 2005, B Term 1999
Data Structures and Programming Techniques
Test 1 Solutions

Your Name: _____________________________________

Your Lab Section: ______________     Your Login Name:_________________

Instructions.   Read each problem carefully before answering. Circle the selected answer(s) or write your solutions in ink in the spaces provided when this is the appropriate action as indicated below for each problem. Write neatly. Each problem is worth 60 points. All parts of each problem have equal point value. Good luck!

  1. a) Among the following collection of terms, circle the three that correspond to the three basic types of member functions that are required for nearly all C++ classes:
    Arrows are used instead of circles here for extremely sophisticated
    technical reasons...
    
    -->Modification member functions     Total order semantics
    
       Overloaded + and * operators      Constant member functions <--
    
       Copy constructors                 Constructors <--         
    
       Overloaded assignment operators   Destructors
    
    

    b) A class named MyClass has exactly five member functions, named MyClass(), clear(), paint(), fill(), and get_color(). Assume that p, q, and r are three objects of type MyClass. Circle all statements among those shown below that would be syntactically incorrect if used within a non-member function of the MyClass class. You may assume that the source file that contains these statements provides access to the MyClass class through the appropriate #include "myclass.h" directive.

    
       p.clear();                        fill(p); <--
    
    -->clear(q);                         MyClass z;
    
    -->p.q;                              z = MyClass; <--
    
       p.get_color();
    
    
    
    
    
    
    
    
    
    

  2. The Polynomial class is similar to the classes considered in HW1 and HW2. The private part of the Polynomial class definition uses an array called data to store a given polynomial's coefficients, as defined in the header file shown here:
    
    class Polynomial {
    public:
      typedef int Coefficient; // data type of each coefficient
      static const int DEFAULT_SIZE = 10; // initial maximum degree
      Polynomial(int initial_size = DEFAULT_SIZE);  // constructor
      Coefficient get_coeff(int power) const; // returns coefficient of power
      size_t get_degree() const; // returns the degree of the polynomial
      friend Polynomial operator +(const Polynomial& p, const Polynomial& q);
      friend Polynomial operator *(const Polynomial& p, const Polynomial& q);
    private:
      Coefficient *data;  // data[k] contains coefficient of k-th power
      size_t degree;      // highest power with nonzero coefficient
      size_t size;        // size of currently allocated data array
    };
    
    

    a) Is the following implementation of get_degree consistent with the above specification of the Polynomial class? Circle the appropriate answer:

    
       size_t Polynomial::get_degree() {
          size_t deg=0;
          for (size_t i=0; i<size; i++)
             if (data[i] != 0)
                deg++;
          return deg;
       }
    
       Answer1: Yes, specification is satisfied 
    
    -->Answer2: No, specification is not satisfied
    
       (this implementation is way off target; the return value equals
        the *total number of nonzero coefficients*, which is not at all
        the same as the degree of the given polynomial)
    
    

    b) Suppose that you wish to write a non-member function sum_of_coeffs that returns the sum of all the coefficients of a given Polynomial object. For example, if f represents the polynomial f(x) = 3x2 + 2x + 1, then sum_of_coeffs(f) should return the value 6 (since 3 + 2 + 1 = 6). The function sum_of_coeffs should accept a single const reference parameter p of type Polynomial. Give the full function header for sum_of_coeffs as it should appear in the appropriate implementation file.

    
       Polynomial::Coefficient sum_of_coeffs(const Polynomial& p)
    
    
    
    
    
    
    
    

  3. The third statement in the program segment shown here calls a generic function named f. The actual output produced by the final statement depends on the definition of f, which is not shown.
    
       float fnum = 3.14;
       float *fptr = &fnum;
       f(fptr);
       cout << *fptr;    // actual output depends on the definition of f
    
    
    For each of the following alternative definitions for the function f, state what would be printed by the final statement in the above program segment if that particular definition of f were used. Any given program would of course include at most one of these possible definitions for f.
    
    a) // 1st definition of f 
       void f(float*& x) {          Answer for a): 1.99
          float *y;
          y = new float;
          *y = 1.99;
          x = y;
       }
       
    
       
    b) // 2nd definition of f
       void f(float* x) {           Answer for b): 3.14
          float *y;
          y = new float;
          *y = 1.99;
          x = y;
       }
       
    
    
    c) // 3rd definition of f
       void f(float* x) {           Answer for c): 1.99
          float *y;
          y = new float;
          *y = 1.99;
          *x = *y;
       }
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    

  4. Consider a function named enlarge that has the following specification:
    
    // void enlarge(int*& array, size_t oldsize, size_t newsize);
    // Precondition: array points to a previously allocated array of size oldsize,
    //   and also newsize > oldsize
    // Postcondition: the size of the allocated array has increased to newsize,
    //   the previous contents of the first oldsize positions of the array remain 
    //   intact, and each remaining position of the array contains the value -1. 
    //   Any dynamic memory that is no longer needed has been released.
    
    
    Fill in the following outline to produce a full implementation of the enlarge function.
    
    void enlarge(int*& array, size_t oldsize, size_t newsize) {
    // check that newsize > oldsize
          assert(newsize > oldsize);
    
    
    
    // declare a new pointer variable newarray and dynamically 
    // allocate the appropriate amount of memory to newarray
          int *newarray;
          newarray = new int[newsize];
    
    
    
    // save the old array contents in the newly allocated memory locations
          for (size_t i=0; i<oldsize; i++)
             newarray[i] = array[i];
    
    
    
    // initialize the remaining positions of newarray to -1
          for (size_t i=oldsize; i<newsize; i++)
             newarray[i] = -1;
    
    
    
    // release any old unneeded memory
          delete[] array;
    
    
    
    // make the old pointer variable point to the newly created structure
          array = newarray;
    
    
    
    }
    
    
    
    
    
    
    

  5. Consider the following incomplete definition of a class named Can:
    
       class Can {
       public:
          typedef int Item;
          Can();
    
          // destructor
          ~Can();     
    
          // copy constructor
          Can(const Can& stuff);  
    
          // overloaded assignment operator
          void operator =(const Can& sourcestuff);  
    
          Item getstuff(size_t pos);
          void putstuff(size_t pos, Item x);
       private:
          Item *data;  // array for storage
          size_t size; // size of currently allocated data array
       };
    
    

    a) Based on the fact that this definition uses dynamic memory, add to the above list of prototypes the full prototypes for the copy constructor, overloaded assignment operator, and destructor functions that should be included in the Can class definition.

    
       Answer is shown above.
    
    

    b) Circle two statements in the following program segment that cause the copy constructor of the Can class to be invoked. Refer to the function headers shown.

    
       Can b, c;
    
       b.fillbox(c);          // void Can::fillbox(Can& formalcan);
    
       printcan(b); <--      // void printcan(Can formalcan);
    
       cancan(c);   <--      // Can cancan(Can& formalcan);
    
       c=b;