CS 509, Advanced Programming II

Fall 2001 - Test 7


  1. 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 C::C();
    Hello from A::A();
    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 C {
      C() { cout << "Hello from C:C()\n"; }
      };
    
    struct A : C {
      A() { cout << "Hello from A:A()\n"; }
      };
    
    struct B : A {
      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:20: warning: unused variable struct B b
    
    $ ./t
    Hello from C:C()
    Hello from A:A()
    Hello from B:B()
    
    $ 
    


  2. Is it necessary to the parent's destructor virtual, or is it enough to just declare the child's destructor virtual? In other words, which of

    struct A { 
      virtual ~A() { }
      };
    
    struct B : A { 
      int i;
      };
    

    or

    struct A { 
      };
    
    struct B : A { 
      virtual ~B() { }
      int i;
      };
    

    is correct? Explain.


    The parent's virtual destructor must be virtual; the declarations

    struct A { 
      virtual ~A() { }
      };
    
    struct B : B { 
      int i;
      };
    

    are correct. The virtual keyword is not inherited up the hierarchy; the declarations

    struct A { 
      };
    
    struct B : B { 
      virtual ~B() { }
      int i;
      };
    

    causes the compiler to use static-type lookup for A's destructor, which is not correct when a pointer or reference to A is actually a pointer or reference to B.


  3. A colleague of yours knew that child classes are generally larger than their parent classes because the children add more members to the set of members defined by the parent. However, your colleague got the idea that it should be possible to make the child smaller than the parent by replacing a member in the parent by a smaller member in the child. To test this hypothesis, your colleague wrote the following program

    #include 
    
    struct A {
      double val;
      };
    
    struct B : A {
      int val;
      };
    
    int main() {
      cout << "sizeof(A) = " << sizeof(A) << "\n";
      cout << "sizeof(B) = " << sizeof(B) << "\n";
      }
    

    The idea being that because an integer is smaller than a double, than B should be smaller than A. However, you colleague was surprised when the program produced the following output:

    sizeof(A) = 8
    sizeof(B) = 16
    

    Ignoring the exact numbers produced, explain why B is larger than A and the error in your colleague's thinking.


    B is larger than A because B contains both the variables val. The error in your colleague's thinking most likely arises from the belief that a redeclaration in the child replaces a declaration in the parent; for example, the declaration int val in the child replaces the declaration double val in the parent. However, a child's redeclaration only hides the parent's declaration; the parent's declaration is still around, and you can use the :: scoping operator to get at it:

    $ cat t.cc
    #include 
    using std::cout;
    
    struct C {
      double val;
    
      C() : val(3.14) { }
      };
    
    
    struct A : C {
    
      int val;
    
      A() : val(42) { }
    
      void print(void) {
        cout << "val = " << val << ", parent's val = " << C::val << "\n";
        }
      };
    
    
    int main() {
      A a;
      a.print();
      }
    
    $ g++ -o t -ansi -pedantic -Wall t.cc
    
    $ ./t
    val = 42, parent's val = 3.14
    
    $ 
    


  4. Declare three classes that present the following interfaces:

    A public:
        void f1(void);
    
    B public:
        void f1(void);
        void f2(void);
      non-public:
        void f3(void);
    
    C public:
        void f1(void);
      non-public:
        void f3(void);
    

    Your class declarations should use inheritance to minimize the total number of member function definitions.


    f1() appears in all three classes, so its declaration should be in the most ancestral of the parent classes. Class A contains only f1(), so it needs to be the parent of the other two classes.

    f2() appears in B but not in C, so C needs to be the parent of B.

    Finally, f3() appears non-publicly in both B and C, which means that f3() should be protected in C. A suitable set of declarations would be

    class A {
      public:
        void f1(void);
      };
    
    class C : public A {
      protected:
        void f3(void);
      };
    
    class B : public C {
      public:
        void f2(void);
      };
    



This page last modified on 19 December 2001.