CS 509, Advanced Programming II

Fall 2001 - Test 7


  1. Explain why a handle for class T required that T have a clone() member function rather than using T's copy constructor.


    Because copy constructors (or constructors of any kind) can't be declared virtual, and the handle has to perform a dynamic-type lookup to find the proper clone() member function.


  2. Suppose classes A, B, and C are related by inheritance. Given the following code

    A * ap = new A;
    ap->f();         // Prints "Hello from A:f()."
    
    ap = new B;
    ap->f();         // Prints "Hello from A:f()."
    
    ap = new C;
    ap->f();         // Prints "Hello from C:f()."
    

    write the class definitions of A, B, and C that are consistent with the code and its documented behavior. You don't need to include the bodies of f(), but the prototype must be complete; also, you should assume that f() correctly prints the name of its containing class.


    The middle call to f() indicates static-type (non-virtual) lookup, while the third call to f() indicates dynamic-type (virtual) lookup. Unfortunately, the f() is either virtual or non-virtual; it can't be both.

    A::f() has to be virtual, otherwise C::f() would never get called. To prevent B::f() from being called, leave it undefined in B; inheritance will provide B with A's copy, effectively simulating static-type lookup.

    $ cat t.cc #include 
    
    struct A {
      virtual void f(void) const { cout << "Hello from A::f().\n"; }
      };
    
    struct B : A {
      };
    
    struct C : A {
      void f(void) const { cout << "Hello from C::f().\n"; }
      };
    
    int main() { 
      A * ap = new A;
      ap->f();         // Prints "Hello from A::f()."
    
      ap = new B;
      ap->f();         // Prints "Hello from A::f()."
    
      ap = new C;
      ap->f();         // Prints "Hello from C::f()."
      }
    
    $ g++ -o t -ansi -pedantic -Wall t.cc
    
    $ ./t
    Hello from A::f().
    Hello from A::f().
    Hello from C::f().
    
    $ 
    


  3. Is it possible to use overloaded functions as arguments to template functions? Explain.


    No, it's not. The problem with overloaded functions serving as template-function arguments is that it's generally impossible to tell which overloaded function is being referenced. A function serving as a parameter appears without arguments, which makes it impossible for the compiler to tell which overloaded function is being referenced. For example:

    void print(const string & s) {
      // whatever
      }
    
    void print(const int i) {
      // whatever
      }
    
    template
    void print_it(T f) {
      // whatever
      }
    
    int main() {
      print_it(print); // which print()?
      }
    


  4. Let classes A, B, and C be related to each other by inheritance. Suppose each class's default constructor body contains a statement that prints a message when when the constructor's called. If the declaration

    B b;
    

    produces the following output:

    Hello from A::A().
    Hello from C::C().
    Hello from B::B().
    

    describe the inheritance relations among the three classes.


    Constructors are called from the top down; that is, a parent's constructor is called before the child's. The sequence of print statements given in the problem is consistent with the declarations

    $ cat t.cc
    #include 
    using std::cout;
    
    struct A {
      A() { cout << "Hello from A::A()\n"; }
      };
    
    struct C : A {
      C() { cout << "Hello from C::C()\n"; }
      };
    
    struct B : C {
      B() { cout << "Hello from B::B()\n"; }
      };
    
    int main() {
      B b;
      }
    
    $ g++ -o t -ansi -pedantic -Wall t.cc
    t.cc: In function int main():
    t.cc:17: warning: unused variable struct B b
    
    $ ./t
    Hello from A::A()
    Hello from C::C()
    Hello from B::B()
    
    $ 
    



This page last modified on 19 December 2001.