CSE390C Sample Final, Winter 2023        handout #7
1. Inheritance Mystery (15 points).  Assuming that the following classes have
   been defined:
        class foo {
        public:
            void f1() {
                cout << "foo 1" << endl;
            }
        
            virtual void f2() {
                cout << "foo 2" << endl;
            }
        
            void f3() {
                cout << "foo 3" << endl;
            }
        };
        
        class bar : public foo {
        public:
            virtual void f1() {
                cout << "bar 1" << endl;
            }
        
            void f3() {
                cout << "bar 3" << endl;
            }
        };
        
        class baz : public bar {
        public:
            void f1() {
                cout << "baz 1" << endl;
            }
        
            virtual void f3() {
                cout << "baz 3" << endl;
            }
        };
        
        class mumble : public baz {
        public:
            void f1() {
                cout << "mumble 1" << endl;
            }
        
            void f2() {
                cout << "mumble 2" << endl;
            }
        
            void f3() {
                cout << "mumble 3" << endl;
            }
        };
And assuming the following variables have been defined:
        foo * var1 = new mumble();
        bar * var2 = new mumble();
        baz * var3 = new mumble();
        foo * var4 = new bar();
        bar * var5 = new baz();
        
In the table below, indicate in the right-hand column the output produced by
the statement in the left-hand column.
        Statement                       Output
        ------------------------------------------------------------
        var1->f1();                     ____________________________
        var2->f1();                     ____________________________
        var3->f1();                     ____________________________
        var4->f1();                     ____________________________
        var5->f1();                     ____________________________
        var1->f2();                     ____________________________
        var2->f2();                     ____________________________
        var3->f2();                     ____________________________
        var4->f2();                     ____________________________
        var5->f2();                     ____________________________
        var1->f3();                     ____________________________
        var2->f3();                     ____________________________
        var3->f3();                     ____________________________
        var4->f3();                     ____________________________
        var5->f3();                     ____________________________
2. Class definition (25 points).  Write a class called string_pair that stores
   a pair of strings that are heap allocated by calling new.  The class should
   have the following member functions:
      string_pair(s1, s2)    constructs a string_pair from the given string
                             values (each should default to an empty string)
      string_pair(other_p)   constructs a string_pair from another string_pair
      get_first()            returns a const reference to the first string
      get_second()           returns a const reference to the second string
      to_string()            returns a text version of the pair separated by a
                             comma and in parentheses, as in (some, text)
   Your class should have a destructor and should override the assignment
   operator.  You must write your class in such a way that all memory allocated
   by your string_pair objects is freed up by calls on delete.  You may assume
   that this class is not part of an inheritance hierarchy.
3. STL Programming (10 points).  Write a function called retain_all that
   takes two sets of integers as parameters and that removes any values in the
   first set that are not found in the second set.  For example, given sets:
        s1: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
        s2: [3, 6, 9, 12, 15, 18, 21, 24]
   If the following call is made:
        retain_all(s1, s2);
   after the call, the sets would store the following values:
        s1: [6, 12, 18]
        s2: [3, 6, 9, 12, 15, 18, 21, 24]
   Notice that s2 is unchanged but s1 now contains only values that are also in
   s2.
4. STL Programming (15 points).  Write a function called convert that
   takes as a parameter a set of strings representing phone numbers and that
   returns a map of strings to sets of strings that represent the same phone
   numbers split into exchange/suffix.  In particular, the phone numbers in the
   set passed to the method will each include a 3-digit exchange followed by a
   dash followed by a four-digit suffix, as in:
        (493-3923, 723-9278, 384-1917, 555-1795, 384-4923, 555-4923, 555-1212,
         723-9823)
   The method should split each string into the 3-digit exchanges that come
   before the dash and the suffixes that come after.  The method should
   construct a map in which the keys are the 3-digit exchanges.  Each such
   exchange will be mapped to a set of 4-digit suffixes.  For the set of phone
   numbers above, the following map would be constructed:
       ((384, (1917, 4923)), (493, (3923)), (555, (1212, 1795, 4923)),
        (723, (9278, 9823)))
   Notice, for example, that three of the phone numbers in the original set
   began with "555".  In the map, the key "555" maps to a set of three elements
   (the three suffixes that came after "555-").
5. STL Programming (10 points).  Write a function called count_shuffles that
   takes a vector of int values as a parameter and that randomly shuffles the
   values until the first half is in sorted order, returning the number of
   shuffles performed.  If the vector has an odd length, then include the
   middle element in the first half that is to be sorted.  For example, if a
   vector called numbers contains this sequence:
        [3, 8, 4, 5, 12, 17, 2, 8, 15, 19, 27, 13, 6]
   then the call count_shuffles(numbers) should randomly shuffle the entire
   vector of numbers until the first half is sorted.  For example, the vector
   might store these values after the function is called:
        [3, 6, 8, 8, 12, 13, 15, 19, 2, 27, 17, 5, 4]
         |                    |   |                |
         +--------------------+   +----------------+
                 sorted                unknown
   Notice that the function will not always end up with the same values in the
   first half because the only requirement is that those values appear in
   sorted order.  We won't know anything about the values in the second half.
   In a sample execution, the count returned was 5251, indicating that it took
   5251 random shuffles to end up with the first half being sorted.
6. Inheritance (20 points).  Define a base class called printable that will
   allow us to override the insertion operator ("<<") just once for all of the
   classes included in this hierarchy.  The base class should have a member
   function called print that takes a reference to an ostream as a parameter.
   It shouldn't return a value.  The base class should be an abstract class and
   the print member function should be a purely virtual function.
   Using the virtual print function, you are to override the insertion operator
   ("<<").  We can create many subclasses that each override print.  You will
   write one such class called quoted_text.  It should have a public
   constructor that takes a string as a parameter.  It should print the string
   surrounded by single quotation marks.  For example, this code:
        printable * p = new quoted_text("hello");
        cout << "p = " << *p << endl;
        delete p;
   should produce the following output:
        p = 'hello'
   You should write your class so that the objects constructed using it do not
   produce a memory leak.  You do not have to worry about a client calling the
   copy constructor or the assignment operator.  Include below your definition
   for the printable class, the overridden insertion operator, and the
   quoted_text class.
7. Short answer (5 points).  Suppose that you want to loop over a sequence of
   string values stored in a structure called "data".  Often we can use a
   foreach loop to do so:
        for (string s : data) {
            ...
        }
   One problem with this approach is that it will make copies of the strings.
   So it is tempting to want to change this to a loop that uses references to
   strings: 
        for (string & s : data) {
            ...
        }
   This will be more efficient in that it won't make copies of the strings, but
   it often doesn't compile even though the other version does.  Why not?
   There are many possible reasons.  Describe one.