Chapter 4
Abstraction Mechanisms, Part 2

Kurt Nørmark
Department of Computer Science, Aalborg University


Abstract
Previous lecture Next lecture
Index References Contents
In this lecture we explore inheritance in C++, in particular multiple inheritance.

Inheritance in C++
Slide Annotated slide Contents Index
References 

Overview of the interesting topics related in inheritance in C++

  • Multiple inheritance

    • More than one superclass (base class) is possible in C++

  • No interfaces in C++

    • Interfaces in Java and C# compensates for the lack of multiple inheritance

  • Virtual functions and pure virtual functions

    • Abstract classes are related to the presence of virtual functions

    • Polymorphic behavior - dynamic binding - relies on access to objects via pointers or C++ references

  • Shared or replicated base classes

    • Shared bases classes are called virtual bases classes

  • Access control to base classes

    • Private, protected and public base classes

Inheritance: Constructors and destructors
Slide Annotated slide Contents Index
References 

Constructors in class hierarchies work as expected in C++

Reference

Reference
  • The C++ Programming Language: Page 306

  • Constructor rules

    • A subclass constructor invoke the superclass constructors - implicitly or explicitly

      • Constructors with parameters must be invoked explicitly

      • Default constructors can be invoked implicitly

    • Bottom up construction and top down destruction

      • First base class constructors, then derived class constructors

      • Multiple base classes are constructed in their declaration order

Program: Constructors and single inheritance.
// Single inheritance - warming up.
// Illustrates how a Base constructor is used in the Derived constructors member initializer.

#include <iostream>
#include <string>

using namespace std;

class Base{
public:
  int i;
  string s;

  Base(int i, string s): i(i), s(s){
  }
};

class Derived: public Base{
public:
  double d;

  Derived(double d, int i, string s): Base(i+1, s+s), d(d){
  }
};

int main(){
  Base b1(1, "AP");
  Derived d1(5.0, 1, "AP");

  cout << b1.i << endl;            // 1
  cout << b1.s << endl << endl;    // AP

  cout << d1.d << endl;            // 5
  cout << d1.i << endl;            // 2
  cout << d1.s << endl;            // APAP

}

Program: Constructors and multiple inheritance.
// Multiple inheritance and constructors.
// Illustrates use of Base constructors in Derived constructor with multiple inheritance.

#include <iostream>
#include <string>

using namespace std;

class Base1{
public:
  int i;
  string s;

  Base1(int i, string s): i(i), s(s){
  }
};

class Base2{
public:
  int j;
  bool b;

  // No default constructor in Base2


  Base2(int j, bool b): j(j), b(b){
  }
};

class Derived: public Base1, public Base2{
public:
  double d;

  Derived(double d, int i, string s):
      Base1(i+1, s+s), Base2(i+2, false), d(d){
  }
};

int main(){
  Derived d1(5.0, 1, "AP");

  cout << d1.d << endl;    // 5
  cout << d1.i << endl;    // 2
  cout << d1.s << endl;    // APAP
  cout << d1.j << endl;    // 3
  cout << d1.b << endl;    // 0 

}

Program: Constructors and multiple inheritance - implicit activation of default constructor in Base.
// Illustrates that the default Base2 constructor may be called implicitly. 
// Illustrates that a default constructor in a base class may be implicitly activated.

#include <iostream>
#include <string>

using namespace std;

class Base1{
public:
  int i;
  string s;

  Base1(int i, string s): i(i), s(s){
  }
};

class Base2{
public:
  int j;
  bool b;

  Base2(): j(11), b(false){             // Now with a default constructor
  }
   
  Base2(int j, bool b): j(j), b(b){
  }
};

class Derived: public Base1, public Base2{
public:
  double d;

  Derived(double d, int i, string s):
      Base1(i+1, s+s),                  // Base2 constructor not activated explictly. 
      d(d){                             //   ... The default constructor in Base2 is activated implicitly.
  }
};

int main(){
  Derived d1(5.0, 1, "AP");

  cout << d1.i << endl;    // 2
  cout << d1.s << endl;    // APAP
  cout << d1.j << endl;    // 11
  cout << d1.b << endl;    // 0
  cout << d1.d << endl;    // 5
}

Program: Constructors and multiple inheritance - order of construction and destruction.
// Demonstration of construction and destruction order.

#include <iostream>
#include <string>

using namespace std;


class Base1{
public:
  int i;
  string s;

  Base1(int i, string s): i(i), s(s){ 
    cout << "Base1 constructor" << endl;
  }

  ~Base1(){
    cout << "Base1 destructor" << endl;
  }
};

class Base2{
public:
  int j;
  bool b;

  Base2(int j, bool b): j(j), b(b){
    cout << "Base2 constructor" << endl;
  }

  ~Base2(){
    cout << "Base2 destructor" << endl;
  }
};



class Derived: public Base1, public Base2{         // Declaration order: Base1 before Base2
public:
  double d;

  Derived(double d, int i, string s):
      Base1(i+1, s+s), Base2(i+2, false), d(d){
        cout << "Derived constructor" << endl;
  }

  ~Derived(){
    cout << "Derived destructor" << endl;
  }

};

int main(){
  Derived d1(5.0, 1, "AP");  // Base1 constructor 
                             // Base2 constructor 
                             // Derived constructor 
                             // Derived destructor 
                             // Base2 destructor 
                             // Base1 destructor 
}

Program: Constructors and multiple inheritance - order of construction and destruction.
// Demonstration of construction and destructor order. Bases re-ordered.

#include <iostream>
#include <string>

using namespace std;


class Base1{
public:
  int i;
  string s;

  Base1(int i, string s): i(i), s(s){ 
    cout << "Base1 constructor" << endl;
  }

  ~Base1(){
    cout << "Base1 destructor" << endl;
  }
};

class Base2{
public:
  int j;
  bool b;

  Base2(int j, bool b): j(j), b(b){
    cout << "Base2 constructor" << endl;
  }

  ~Base2(){
    cout << "Base2 destructor" << endl;
  }
};



class Derived: public Base2, public Base1{        // Base classes re-ordered
public:
  double d;

  Derived(double d, int i, string s):
      Base1(i+1, s+s), Base2(i+2, false), d(d){
        cout << "Derived constructor" << endl;
  }

  ~Derived(){
    cout << "Derived destructor" << endl;
  }

};

int main(){
  Derived d1(5.0, 1, "AP");  // Base2 constructor 
                             // Base1 constructor 
                             // Derived constructor 
                             // Derived destructor 
                             // Base1 destructor 
                             // Base2 destructor 
}

Inheritance: Copying and slicing
Slide Annotated slide Contents Index
References 

'Real object-oriented programming' relies on reference semantics (use of pointers or C++ references to objects) rather than value semantics (copying of objects)

Reference
  • The C++ Programming Language: Page 307

  • In the example programs: The class B is derived from class A

    • We copy an instance of B to a variable of type A

    • No pointer types or reference types are involved

    • The B part of the object is lost

    • We say that it is sliced

Program: Illustration of object slicing.
// Class B inherits from A. 
// Illustration of slicing when we copy B object to a variable of static type A.

#include <iostream>
#include <string>

using namespace std;

class A {
public:
  int a;

  A(): a(3){
  }

  virtual int op(){
     cout << "A: operation" << endl;
     return a;
  }
};

class B: public A {
public:
  int b;

  B():  b(5){
  }

  int op(){
     cout << "B: operation" << endl;
     return b;
  }
};

int f(B &x){
  A  y = x,        // The B object x is sliced: The B part is lost during copy construction.
    *z = &x,       // No slicing. The B object is accessed via pointer.
    &w = x;        // No slicing. The B object is accessed via a reference.

  cout << y.op() << endl;    // 3.  A operation. y has been sliced to an A object.
  cout << z->op() << endl;   // 5.  B operation. z points at a B object.
  cout << w.op() << endl;    // 5.  B operation. w is an alias to aB from main.
}

int main(){
  B aB;
  f(aB);
}

Program: Program output.
A: operation
3
B: operation
5
B: operation
5

Program: Does this variant change the game?.
// Class B inherits from A. Illustration of slicing.
// A minor variant of the previous program.

#include <iostream>
#include <string>

using namespace std;

class A {
public:
  int a;

  A(): a(3){
  }

  virtual int op(){
     cout << "A: operation" << endl;
     return a;
  }
};

class B: public A {
public:
  int b;

  B():  b(5){
  }

  int op(){
     cout << "B: operation" << endl;
     return b;
  }
};

int f(A &x){       // Was   int f(B &x)   in the earlier version.
  A  y = x,        // The B object x is sliced: The B part is lost.
    *z = &x,       // No slicing. The B object is accessed via pointer.
    &w = x;        // No slicing. The B object is accessed via a reference.

  cout << y.op() << endl;    // 3.  A operation. y has been sliced to an A object.
  cout << z->op() << endl;   // 5.  B operation. z points at a B object.
  cout << w.op() << endl;    // 5.  B operation. w is an alias to aB from main.
                             // So did anything change?   NO  
}

int main(){
  B aB;
  f(aB);
}

Program: Program output.
A: operation
3
B: operation
5
B: operation
5

Exercise 4.2. Slicing an object during parameter passing.

On the program that belongs to the accompanying slide the parameter of the function f is passed by C++ reference.

Now assume that the parameter is passed by value:

int f(A x){
  A  y = x,
    *z = &x,
    &w = x; 

  cout << y.op() << endl;    
  cout << z->op() << endl;   
  cout << w.op() << endl;    
}

What will be the output of the 3 output statements at the bottom of f. Please predict the result before you run the program.

Member access control: private, public and protected
Slide Annotated slide Contents Index
References 

The use of private, public, and protected access control in C++ to members is similar to the access control in other object-oriented languages

Reference
  • The C++ Programming Language: Page 402, 849

  • Observations

    • Access control is applied uniformly to names

      • Functions

      • Variables

      • Types

      • And others

    • private/public/protected are NOT used as modifiers of individual members

    • Access control via private/public/protected can be augmented with use of friends

References

Virtual Functions
Slide Annotated slide Contents Index
References 

A virtual member function in C++ works as expected:

The dynamic type of the receiver controls which function to call.

Destructors may also be virtual, §12.4.2.

Reference
  • The C++ Programming Language: Page 310

  • Conditions for getting polymophic behavior in C++:   obj.f()

    • A virtual member function f must be used on an object obj

    • The object obj must be accessed via a pointer or a reference

  • Avoiding polymorphic behavior with a virtual function f

    • Activate f with a scope operation: C::f(...)

The class B is a specialization of class A
To see this image you must download and install the SVG plugin from Adobe.In Firefox please consultthis page.

Program: Virtual versus non-virtual functions in an AB class hierarchy.
// Class B inherits from A. We activate virtual (vf) and non-virtual (f) functions
// on a parameter passed by value, by a pointer, and by reference.

#include <iostream>
#include <string>

using namespace std;

class A {
private:
   double a;
public:
  virtual void vf(double d){
    cout << "virtual vf in A" << endl;
  }

  void f(double d){
    cout << "f in A" << endl;
  }
};

class B : public A {
private:
  double b;
public:
  void vf(double d){
    cout << "virtual vf in B" << endl;
  }

  void f(double d){
    cout << "f in B" << endl;
  }
};

int f1(A a){
  a.vf(1.0);       // virtual vf in A. Why?  Because a is sliced during parameter passing.
  a.f(2.0);        // f in A
  cout << endl;
}

int f2(A *ap){
  ap->vf(3.0);     // virtual vf in B
  ap->A::vf(3.0);  // vf in A
  ap->f(4.0);      // f in A
  cout << endl;
}

int f3(A &ar){
  ar.vf(5.0);      // virtual vf in B
  ar.A::vf(3.0);   // vf in A
  ar.f(6.0);       // f in A
  cout << endl;
}

int main(){
  B  b1;
             // We pass b1 to f1, f2 and f3
  f1(b1);    //    ... by value             (a copy of b1 is passed)
  f2(&b1);   //    ... a pointer by value   (a pointer to b1 is passed)
  f3(b1);    //    ... by C++ reference     (b1 as such is passed)
}

Program: Program output.
virtual vf in A
f in A

virtual vf in B
virtual vf in A
f in A

virtual vf in B
virtual vf in A
f in A

Destructors and Inheritance - Virtual Destructors
Slide Annotated slide Contents Index
References 

A class with virtual functions should always have a virtual destructor

Reference
  • Effective C++, Third edition: Item 7

Program: Base class A and derived class B with non-virtual destructors - motivation.
// Illustration of non-virtual destructors. Motivation for next version of the program.
#include <iostream>

using namespace std;

class A {
private:
   double a;
public:

  A(double a): a(a){};
  ~A(){cout << "A destructor" << endl;}
  // Class A is assumed to have virtual functions.
  //....
};

class B : public A {
private:
  double b;
public:
  B(double b): A(b-1), b(b){};
  ~B(){cout << "B destructor" << endl;}
  //...
};

void f(){
  A *a1 = new B(5.0);             // a1 points to a B object on the free store.
  B b1(6.0);                      // b1 contains a B object
  
  // Work on a1 and b1.
  // ....

  delete a1;                      // The destructor in A called 
                                  // The B destructor is not called. LEAK and PROBLEMS.
                                  // The destructor in A is not virtual.

  cout << "Now end of f" << endl;
  // B destructor   (object b1)   // b1 goes out of scope. It is destructed at 
  // A destructor   (object b1)   // ... both B and A level.
}  

int main(){
  f();
}

Program: Program output.
A destructor
Now end of f
B destructor
A destructor

Program: Base class A and derived class B with virtual destructors.
// Illustration of virtual destructors.
#include <iostream>

using namespace std;

class A {
private:
   double a;
public:

  A(double a): a(a){};
  virtual ~A(){cout << "A destructor" << endl;}
  // Class A is assumed to have virtual functions.
  //....
};

class B : public A {
private:
  double b;
public:
  B(double b): A(b-1), b(b){};
  ~B(){cout << "B destructor" << endl;}
  //...
};

void f(){
  A *a1 = new B(5.0);
  B b1(6.0);
  
  // Work on a1 and b1.
  // ....

  delete a1;                           // The destructor in B is called.
                                       // The destructor in A is called.

  cout << "Now end of f" << endl;
  // B destructor   (object b1)
  // A destructor   (object b1)
}  

int main(){
  f();
}

Program: Program output.
B destructor
A destructor
Now end of f
B destructor
A destructor

Program: Base class A and derived class B and C with virtual destructors.
// Illustration of virtual destructors in A-B-C class hierarcy. 
// We only need to declare a virtual destructor in the base class. 
#include <iostream>

using namespace std;

class A {
private:
   double a;
public:

  A(double a): a(a){};
  virtual ~A(){cout << "A destructor" << endl;}
  // Class A is assumed to have virtual functions.
  //....
};

class B : public A {
private:
  double b;
public:
  B(double b): A(b-1), b(b){};
  ~B(){cout << "B destructor" << endl;}
  //...
};

class C : public B {
private:
  double c;
public:
  C(double b): B(b-1), c(b){};
  ~C(){cout << "C destructor" << endl;}
  //...
};

void f(){
  A *a1 = new C(5.0);
  
  // Work on a1
  // ....

  delete a1;                        // The destructor in C is called.
                                    // The destructor in B is called.
                                    // The destructor in A is called.
}  

int main(){
  f();
}

Program: Program output.
C destructor
B destructor
A destructor

Hiding inherited names
Slide Annotated slide Contents Index
References 

Reference
  • Effective C++, Third edition: Item 33

The programs on this slide is the starting point of an exericse

The topic is hiding inherited names

Program: A class B inherits two virtual, overloaded functions from A - straightforward - no problems.
#include <iostream>
#include <string>

using namespace std;

class A {
private:
   double a;
public:
  virtual void vf(double d){                // Virtual vf(double)
    cout << "virtual vf(double) in A: " 
         << d << endl;
  }

  virtual void vf(){                        // Virtual vf()
    cout << "virtual vf() in A" 
         << endl;
  }

};

class B : public A {
private:
  double b;
public:
  // B member functions here 
                                            // B inherites vf(double) and vf()
};


int main(){
  B *b = new B();
  
  b->vf();                                  // OK. A::vf() is called
  b->vf(5.0);                               // OK. A::vf(double) is called
}

Exercise 4.3. Hiding inherited names

This is an exercise about an apparently weird happening in a C++ program with overloaded functions. It is good for you to wonder - and realize what happens. The solution is not not necessarily easy to find 'without cheating'...

The program shown together with this slide illustrates a simple situation when the derived class B inherits two virtual functions from a base class A. Everething is fine.

Redefine (override) vf in the derived class B, and keep the main function unchanged? Is everything still fine?

Discuss potential problems. Can you explain your experiences. Can you find a way to solve the problems you may have encountered?

If you have the book Effective C++, Third edition you are encouraged to read item 33.

Abstract Classes
Slide Annotated slide Contents Index
References 

An abstract class in C++ is a class with one or more pure virtual functions

Reference
  • The C++ Programming Language: Page 313

  • Abstract class

    • A class is abstract if it has one or more pure virtual functions

    • No objects can be created from an abstract class

    • No abstract modifiers are used on the class, nor on member functions

  • Pure virtual functions

    • A pure virtual function is marked with a 0

      • virtual void vf(double d) = 0;

Program: A variant were vf is a pure virtual function in class A.
// Illustration of pure virtual functions in continuation of 
// the already shown example of virtual functions. Does not compile as shown.
// If red parts are eliminated, the program compiles.

#include <iostream>
#include <string>

using namespace std;

class A {                              // An abstract class
private:
   double a;
public:
  virtual void vf(double d) = 0;                 // A pure virtual function

  void f(double d){                              // Statically bound function f in A
    cout << "f in A" << endl;
  }
};

class B : public A {                              
private:
  double b;
public:
  void vf(double d){                             // Definition of the virtual function 
    cout << "virtual vf in B" << endl;
  }

  void f(double d){                              // Statically bound function f in B.
    cout << "f in B" << endl;
  }
};

int f1(A a){       // error: cannot declare parameter  a  to be of abstract type  A.
    a.vf(1.0);       
    a.f(2.0);        
    cout << endl;
}

int f2(A *ap){
  ap->vf(3.0);     // virtual vf in B
  ap->f(4.0);      // f in A
  cout << endl;
}

int f3(A &ar){
  ar.vf(5.0);      // virtual vf in B
  ar.f(6.0);       // f in A
  cout << endl;
}

int main(){
  A a1;            // error: cannot declare variable  a1  to be of abstract type  A.

  B  b1,
    *b2 = new B();

  f1(b1);          // error: cannot allocate an object of abstract type 'A'
  f2(b2);
  f3(b1);
}

What about interfaces in C++?
Slide Annotated slide Contents Index
References 

Interfaces are well-known from Java and C#, but they do not appear in C++

  • Why do Java and C# have Interfaces?

    • Because Java and C# do not support multiple inheritance

    • Interfaces are the poor man's remedy for not having multiple inheritance

  • How do we make an Interface in C++?

    • Program a class with only pure virtual functions

Program: A sample C# interface.
// This is a C# interface.

public enum GameObjectMedium {Paper, Plastic, Electronic}

public interface IGameObject{

  int GameValue{
    get;
  }

  GameObjectMedium Medium{
    get;
  }
}

Program: The C++ counterpart to the C# Interface.
// A C++ Interface-like abstract class

enum GameObjectMedium {Paper, Plastic, Electronic};

class IGameObject{
public:
  virtual int getGameValue() = 0;
  virtual GameObjectMedium getMedium() = 0;
};

Program: A C++ class that 'implements the interface' and uses the resulting class.
// An 'implementation' of the 'interface-like' abstract class in C++

#include <iostream>

enum GameObjectMedium {Paper, Plastic, Electronic};

class IGameObject{
public:
  virtual int getGameValue() = 0;
  virtual GameObjectMedium getMedium() = 0;
};

class GameObject: public IGameObject{
public:
  int getGameValue(){
    return 1;
  }

  virtual GameObjectMedium getMedium(){
    return Electronic;
  }
};

int main(){
  IGameObject *ig = new GameObject();

  std::cout << ig->getGameValue() << std::endl;  // 1
  std::cout << ig->getMedium() << std::endl;     // 2
}

What about nested classes in C++?
Slide Annotated slide Contents Index
References 

Classes can be nested in C++

Similar to inner static classes in Java

No consequences for the nesting of instances of the involved classes

Reference
  • The C++ Programming Language: Page 851-852

  • "The members of a member class have no special access to members of an enclosing class"

    • According to Stroustrup page 851

    • In g++ instances of an inner class can access private members of instances of an outer class

      • This seems to be reasonable because class Inner is nested in class Outer

  • If an instance i of Inner needs access to the instance o of Outer, o must passed as a parameter to the constructor of Inner, or to an Innner method

    Program: Class Outer that contains class Inner - does not compile.
    // Reproduced from page 851-852 of "The C++ Programming Language", 3ed version.
    // Intended to show that instances of an inner class does not have access to private members
    // in instances of an outer class (as described in "The C++ Programming Language"). 
    // This cannot be reproduced with use of g++.
    // Does not compile. 
    
    #include <iostream>
    #include <string>
    
    class Outer{
    private:
      typedef int T;             // Type T and int i are private in Outer
      int i;
    public:
      int i2;
      static int s;
    
      class Inner{
      private:
        int x;
        T y;                     // Expected error: Outer::T is private. g++ can use private type in surrounding class.
      public:
        void fi(Outer *op, int v);
      };
    
      int fo(Inner* ip);
    };
    
    void Outer::Inner::fi(Outer *op, int v){
      op->i = v;                 // Expected error: Outer::i is private. g++ can use private variable in surrounding class.
      op->i2 = v;                // OK - i2 is public
    }
    
    int Outer::fo(Inner* ip){
      ip->fi(this,2);             // OK - Inner::fi is public
      return ip->x;               // error: Inner::x is private. Even g++ cannot use a private variable in inner class.
    }
    
    int main(){
      Outer o;  
      Outer::Inner i;
    
      i.fi(&o, 5);
      o.fo(&i);
    }

    Program: Class Outer that contains class Inner - friends of each other.
    // The starting point reproduced from page 851-852 of "The C++ Programming Language", 3ed version.
    // With mutual friendship between Outer and Inner classes.
    // Solves visibility problems from earlier version of the program.  Compiles.
    
    #include <iostream>
    #include <string>
    
    class Outer{
      friend class Inner;                       // Inner is a friend of Outer
    private:
      typedef int T;
      int i;
    public:
      int i2;                                   
      static int s;
    
      class Inner{
        friend class Outer;                     // ... and Outer is a friend of Inner
      private:
        int x;
        T y;
      public:
        void fi(Outer *p, int v);
      };
    
      int fo(Inner* p);
    };
    
    void Outer::Inner::fi(Outer *op, int v){
      op->i = v;                                // Now i is visible, for sure
      op->i2 = v;                               //    - because Inner is a friend of Outer
    }
    
    int Outer::fo(Inner* ip){
      ip->fi(this,2);
      return ip->x;                             // Now x in Inner is visible
    }                                           //    - because Outer is a friend of Inner
                                                
    int main(){
      Outer o;  
      Outer::Inner i;
    
      i.fi(&o, 5);
    }

    Related examples below ...

    Program: A variant where class Inner is private in Outer - does not compile.
    // In starting point reproduced from page 851-852 of "The C++ Programming Language", 3ed version.
    // With mutual friendship between Outer and Inner classes.
    // In this version class Inner is private in class Outer.  Does not compile.
    
    #include <iostream>
    #include <string>
    
    class Outer{
      friend class Inner;                      
    private:
      typedef int T;
      int i;
    
      class Inner{              // Inner is now private in Outer
      friend class Outer;                      
      private:
        int x;
        T y;                    
      public:
        void fi(Outer *p, int v);
      };
    
      Inner ii;
    
    public:
      int i2;
      static int s;
    
      int fo(Inner* p);
    };
    
    void Outer::Inner::fi(Outer *op, int v){
      op->i = v;                               
      op->i2 = v;
    }
    
    int Outer::fo(Inner* ip){
      ii.fi(this,3);
      ip->fi(this,2);
      return ip->x;                            
    }
    
    int main(){
      Outer o;  
      Outer::Inner i;           // error:  class Outer::Inner  is private
    
      i.fi(&o, 5);
    }

    Program: Inner attempts to access to non-static variable in Outer - does not compile.
    // Inspired from page 851 of "The C++ Programming Language", 3ed version.
    // Can Inner see an instance variable in Outer?  By just looking into the outer scope. 
    // The answer is no.  The variable must be static for this to be succesful.
    
    #include <iostream>
    #include <string>
    
    class Outer{
    private:
      int private_i;
    
    public:
      int public_i;
    
      class Inner{
      public:
        void fi(Outer *p);
      };
    
    };
    
    void Outer::Inner::fi(Outer *p){
      int local1 = private_i * 2;          //  error: invalid use of nonstatic data member  Outer::private_i
      int local2 = public_i * 3;           //  error: invalid use of nonstatic data member  Outer::public_i
    
      std::cout << local1 << ", " << local2 << std::endl; 
    }
    
    
    int main(){
      Outer o;  
      Outer::Inner i;
    
      i.fi(&o);
    }

    Program: Problems solved - This program compiles.
    // Inspired from page 851 of "The C++ Programming Language", 3ed version.
    // Accessing private_i and public_i as instance variables in Outer.
    
    #include <iostream>
    #include <string>
    
    class Outer{
    private:
      int private_i;
    
    public:
      int public_i;
    
      class Inner{
      public:
        void fi(Outer *p);
      };
    
    };
    
    void Outer::Inner::fi(Outer *p){
      int local1 = p->private_i * 2;      // g++: Access is possible despite private_i is private in Outer. 
      int local2 = p->public_i * 3;       //  - same issue as in the earlier version of the program.
    
      std::cout << local1 << ", " << local2 << std::endl; 
    }
    
    
    int main(){
      Outer o;  
      Outer::Inner i;
    
      i.fi(&o);
    }

    What about prevention of derivation in C++
    Slide Annotated slide Contents Index
    References 

    Reference
    • Effective C++, Third edition: page 43. Item 7

    In Java a class can be final

    In C# a class can be sealed

    Such means of expression does not exist in C++

    Pointer to members
    Slide Annotated slide Contents Index
    References 

    A pointer to a member function corresponds to a function pointer

    Useful for activation of an unknown/arbitray method among several possible of the same signature

    The two special C++ operators   .*  and   ->*   bind a pointer to member to a specific object (accessed without or via a pointer respectively)

    Reference
    • The C++ Programming Language: Page 418, 853

    Program: Class variant of class Point with several different move functions.
    class Point {
    private: 
      double x, y;
    
    public:
      Point(double, double);
      Point();
      double getx () const;
      double gety () const;
      void move_relative(double, double);   // Three move
      void move_absolute(double, double);   // functions with the 
      void move_funny(double, double);      // same signature
    
      double distance_to(Point);
    };
    
    std::ostream& operator<<(std::ostream&, const Point&);

    Program: Implementation of class Point and the tree move methods - not important for the example.
    #include <cmath>
    #include <iostream>
    #include "point.h"
    
    Point::Point(double x_coord, double y_coord): x(x_coord), y(y_coord){
    }
    
    Point::Point(): x(0.0), y(0.0){
    }
    
    double Point::getx () const{
      return x;
    }
    
    double Point::gety () const{
      return y;
    }
    
    void Point::move_relative(double dx, double dy){
        x += dx; y += dy;
    }
    
    void Point::move_absolute(double x1, double y1){
        x = x1; y = y1;
    }
    
    void Point::move_funny(double dx, double dy){
        x += dx*2; y -= dy;
    }
    
    double Point::distance_to(Point p){
        return sqrt((x - p.x) * (x - p.x) + (y - p.y) * (y - p.y));
    }
    
    std::ostream& operator<<(std::ostream& s, const Point& p){
      return s << "(" << p.getx() << "," << p.gety() << ")" ;
    }

    Program: A program that illustrates pointer to Point member functions.
    // Ilustration of pointer to members.
    
    #include <iostream>
    #include <string>
    #include "point.h"
    
    // Typedef the Point move signature:
    typedef void (Point::* Ptr_to_move)(double, double);  // Ptr_to_move becomes the name of the function signature:
                                                          // (double, double) -> void
                                                          // Special declarator syntax:   Class::*   pointer to member in Class.
    void f(int selector, Point *p, Point q){
      Ptr_to_move mv;                                     // A variable which can be assigned to a pointer to a member
                                                          // to a Point move function.
      // Assign the member pointer mv:
      switch(selector){
        case 1: {mv = &Point::move_relative; break;}      // mv becomes a member pointer to move_relative.
        case 2: {mv = &Point::move_absolute; break;}      // mv becomes a member pointer to move_absolute.
        case 3: {mv = &Point::move_funny; break;}         // mv becomes a member pointer to move_funny.
      }
    
      (p->*mv)(1,1);                                      // Call the move function refered from mv on Point pointer p.
      (q.*mv)(2,2);                                       // Call the move function on q.
    
      std::cout << "In f, p: " << *p << std::endl;        // (2,3)
      std::cout << "In f, q: " << q <<  std::endl;        // (5,6)
    }
    
    int main(){
      using namespace std;
    
      Point p1(1,2),
            p2(3,4);
    
      f(1, &p1, p2);                                      // p1 is moved relatively, because 1 is passed as selector to f.
      cout << "In main, p1: " << p1 << endl;              // (2,3)  
      cout << "In main, p2: " << p2 << endl;              // (3,4)  - p2 not affected, of course, because it is passed by value.
    }

    It is also possible to estabslish a pointer to a data member

    The notation associated with pointers to members is a challenge, at least at first sight

    Multiple inheritance - issues and C++ solutions
    Slide Annotated slide Contents Index
    References 

    Let us understand the major reason why multiple inheritance is considered problematic

    Class B is a subclass of class A
    To see this image you must download and install the SVG plugin from Adobe.In Firefox please consultthis page.

    • Concrete problem

      • In a C object ac: Which x does ac.x refer to?

    • General problems

      • The name clash problem: Does x in C refer to the x in A or the x in B?

      • The selection problem: Do we have means in C to select either x in A or x in B?

      • The combination problem: Can x in A and x in B be combined to a single x in C?

      • The replication problem: Is there one or two x pieces in C?

    Multiple inheritance - issues and C++ solutions
    Slide Annotated slide Contents Index
    References 

    Let us understand the major reason why multiple inheritance is considered problematic

    Class B is a subclass of class A
    To see this image you must download and install the SVG plugin from Adobe.In Firefox please consultthis page.

    • Concrete problem

      • In a C object ac: Which x does ac.x refer to?

    • General problems

      • The name clash problem: Does x in C refer to the x in A or the x in B?

        • The programmer decides. Use the scope operator: A::x or B::x

      • The selection problem: Do we have means in C to select either x in A or x in B?

        • Yes - as already seen

      • The combination problem: Can x in A and x in B be combined to a single x in C?

        • No - but some control is possible via virtual bases

      • The replication problem: Is there one or two x pieces in C?

        • There are two x variables in a C object

    Multiple inheritance: Ambiguities
    Slide Annotated slide Contents Index
    References 

    The scope operator can be used to solve ambiguities in multiple base classes

    Use of the function operator() is ambiguous
    To see this image you must download and install the SVG plugin from Adobe.In Firefox please consultthis page.

    Program: Ambiguity - the compiler locates the problem.
    // A very simple illustration of an ambiguity in 
    // a multiple-inheritance situation.
    
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class A {
    public:
      int data;
    
      int operation(){
         cout << "A: operation" << endl;
         return data;
      }
    };
    
    class B {
    public:
      int data;
    
      int operation(){
         cout << "B: operation" << endl;
         return data;
      }
    };
    
    
    class C: public A, public B{
    
    };
    
    int f(C* c){
      return c->operation();  
                              // Compiler: 
                              // error: request for member operation is ambiguous
                              // error: candidates are: int B::operation()
                              // error:                 int A::operation()
    }
    
    
    int main(){
      f(new C());
    }

    Program: Ambiguity resolution in the client of C.
    // How to use the scope operator to resolve the ambiguity.
    
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class A {
    public:
      int data;
    
      int operation(){
         cout << "A: operation" << endl;
         return data;
      }
    };
    
    class B {
    public:
      int data;
    
      int operation(){
         cout << "B: operation" << endl;
         return data;
      }
    };
    
    class C: public A, public B{
    };
    
    int f(C* c){
       int r1 = c->A::operation(),
           r2 = c->B::operation();
       return r1 + r2;     
    }
    
    
    int main(){
      f(new C());   // OUTPUT:
                    // A: operation
                    // B: operation
    }

    Program: Involving polomorphism - but there is a problem.
    // We introduce a function called operation in C which activates the
    // operation in both A and B. We do NOT get the intended results. 
    // Why?  The methods are not virtual.
    
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class A {
    public:
      int data;
    
      int operation(){
         cout << "A: operation" << endl;
         return data;
      }
    };
    
    class B {
    public:
      int data;
    
      int operation(){
         cout << "B: operation" << endl;
         return data;
      }
    };
    
    
    class C: public A, public B{
    public: 
      int operation(){   
         cout << "C: operation" << endl;
         int r1 = A::operation(),
             r2 = B::operation();
         return r1 + r2;     
      }
    
    };
    
    int f(A* obj){
      int res = obj->operation();
      return res;
    }
    
    
    int main(){
      A *obj = new C(); // Polymorphism:
                        // A variable of static type A refers to a C-object
      f(obj);           // ACTUAL OUTPUT:
                        //  A: operation. 
                        // WANTED OUTPUT:
                        //  C: operation. 
                        //  A: operation. 
                        //  B: operation. 
                        // What is the problem?  The methods are not virtual.
    }

    Program: Involving polomorphism - problem solved.
    // The solution to the problem just encountered. The function members
    // named operation in class A and B must be virtual.
    
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class A {
    public:
      int data;
    
      virtual int operation(){
         cout << "A: operation" << endl;
         return data;
      }
    };
    
    class B {
    public:
      int data;
    
      virtual int operation(){
         cout << "B: operation" << endl;
         return data;
      }
    };
    
    
    class C: public A, public B{
    public: 
      int operation(){    
         cout << "C: operation" << endl;
         int r1 = A::operation(),
             r2 = B::operation();
         return r1 + r2;     
      }
    
    };
    
    int f(A* obj){
      int res = obj->operation();
      return res;
    }
    
    
    int main(){
      A *obj = new C();
      f(obj);   // OUTPUT:
                // C: operation
                // A: operation
                // B: operation
    }

    Replicated base class
    Slide Annotated slide Contents Index
    References 

    Need for B and C to have separate A parts in a D-object

    Reference
    • The C++ Programming Language: Page 394

    The base class A is replicated
    To see this image you must download and install the SVG plugin from Adobe.In Firefox please consultthis page.

    Program: Illustration of replication of class A.
    // Illustration of an ambiguity problem when we
    // deal with a replicated base class.
    
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class A {
    public:
      int a;
      A(int a): a(a){}
    };
    
    class B : public A {
    public:
      int b;
      B(): b(1), A(5) {}
    };
    
    class C : public A {
    public:
      int c;
      C(): c(2), A(7){}
    };
    
    class D : public B, public C {
    public:
      int d;
      D(): d(9){}    // use default constructors of B and C
    };
    
    
    int f(D &x){
    //cout << x.a << endl;     // error: request for member  a  is ambiguous
      cout << x.B::a << endl;  // 5
      cout << x.C::a << endl;  // 7
      cout << x.b << endl;     // 1 
      cout << x.c << endl;     // 2
      cout << x.d << endl;     // 9
    
    }
    
    int main(){
      D d;
      f(d);
    }

    Program: Program output.
    5
    7
    1
    2
    9

    Is repeated inheritance possible?
    Slide Annotated slide Contents Index
    References 

    Is it possible to shortcut the replication such that D inherits twice from A?

    The base class A is replicated
    To see this image you must download and install the SVG plugin from Adobe.In Firefox please consultthis page.

    Program: Attempting repeated inheritance in C++.
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class A {
    public:
      int a;
      A(int a): a(a){}
    };
    
    class D : public A, public A {  // error: duplicate base type A invalid
    public:
      int d;
      D(): d(9){}  
    };
    
    
    int f(D &x){
      // problem: how should we select one or the other A branch?
      cout << x.d << endl;     
    }
    
    int main(){
      D d;
      f(d);
    }

    Is repeated inheritance possible? No!
    Slide Annotated slide Contents Index
    References 

    Is it possible to shortcut the replication such that D inherits twice from A?

    The base class A is replicated
    To see this image you must download and install the SVG plugin from Adobe.In Firefox please consultthis page.

    In C++, the answer is no

    The problem is is how to address the two different A parts in a D object

    Shared base class: Virtual base
    Slide Annotated slide Contents Index
    References 

    Per default you get replicated base classes in C++

    It is also possible for B and C to to share their A parts in a D-object

    Reference
    • The C++ Programming Language: Page 396

    Reference

    The base class A is replicated
    To see this image you must download and install the SVG plugin from Adobe.In Firefox please consultthis page.

    Program: Illustration of shared, virtual, base class A.
    // Illustration of virtual base classes.
    
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class A {
    public:
      int a;
      A(): a(8){}
      A(int a): a(11){}
    };
    
    class B : public virtual A {
    public:
      int b;
      B(): b(1){}
    };
    
    class C : public virtual A {
    public:
      int c;
      C(): c(2){}
    };
    
    class D : public B, public C {
    public:
      int d;
      D(): d(9){}    // use default constructors of B and C
    };
    
    
    int f(D &x){
      cout << x.a << endl;     // 8  - not ambiguous, only one a in x. 
      cout << x.B::a << endl;  // 8
      cout << x.C::a << endl;  // 8
      cout << x.b << endl;     // 1 
      cout << x.c << endl;     // 2
      cout << x.d << endl;     // 9
    }
    
    int main(){
      D d;
      f(d);
    }

    Program: Program output.
    8
    8
    8
    1
    2
    9

    Every base class of a given name that is specified to be virtual will be represented by a single object of that class, §15.2.4

    Base class access
    Slide Annotated slide Contents Index
    References 

    A base class in C++ can be either public, protected, or private

    Why is member access control in the individual classes A and B not sufficient?

    Reference
    • The C++ Programming Language: Page 405

    The class B is a specialization of class A
    To see this image you must download and install the SVG plugin from Adobe.In Firefox please consultthis page.

    Base class access
    Slide Annotated slide Contents Index
    References 

    A base class in C++ can be either public, protected, or private

    Reference
    • The C++ Programming Language: Page 405

    The class B is a specialization of class A
    To see this image you must download and install the SVG plugin from Adobe.In Firefox please consultthis page.

    • Are the public members in A also always public in B?

      • In the 'normal case' they are - public base

      • In a more 'restricted case' they are not - private or protected bases

    • When is more restricted access control to base classes useful?

      • In some contexts the public interface of A will be part of the public interface of B

      • In other cases, A is used internally in B, and A should not affect the client interface of B

        • B is implemented in terms of A

        • B is not an A

    Base class access - the C++ rules
    Slide Annotated slide Contents Index
    References 

    The class B is a specialization of class A
    To see this image you must download and install the SVG plugin from Adobe.In Firefox please consultthis page.

    Reference
    • The C++ Programming Language: Page 406

    • A is a private base class of B:

      • Public and protected members in A becomes private in B

      • The default case for classes

    • A is a protected base class of B:

      • Public members in A becomes protected in B

    • A is a public base class of B:

      • Public members in A will also be public in B

      • This is the 'normal case' - but not the default case for classes

    Friends of B are on equal footing to members of B

    Upcasting to a private or protected base A is restricted

    See also the The C++ Programming Language formulations of the rules page 406

    Base class access - Examples
    Slide Annotated slide Contents Index
    References 

    We show a few ABC examles that use base class access specifiers

    A mariage of convenience between B and C

    The base class A is replicated
    To see this image you must download and install the SVG plugin from Adobe.In Firefox please consultthis page.

    Program: Class D inherits privately from B and publically from C.
    // A class D with both a private and public base class.
    // A very simple illustration of the practical consequences. 
    
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class B {
    private:
      int b;
    public:
      B(int b): b(b){}
      void Bop(){                                   // Bop is public in B
        cout << "Bop()" << endl;
      }
    
    };
    
    class C {
    private:
      int c;
    public:
      C(int c): c(c){}
      void Cop(){                                   // Cop is public in C                  
        cout << "Cop()" << endl; 
      }
    };
    
    class D : private B, public C {
    private:
      int d;
    public:
      D(int b, int c, int d): B(b), C(c), d(d){} 
      void Dop(){                                   // Dop is public in D
        Bop();                                      // OK!   Bop used inside D.
        Cop();                                      // OK    Cop is or course visible.
      }
    
    };
    
    
    int f(D &aD){
    // aD.Bop();                                    // Compile error: void B::Bop() is inaccessible in aD's client interface
    
      aD.Cop();                                     // Output: Cop()
    
      aD.Dop();                                     // Output: Bop() Cop()
    }
    
    int main(){
      D d(1,2,3);
      f(d);
    }

    Program: Same setup: Which variables can access which objects.
    // Program indended to illustrate and discuss the rules of polymorphism 
    // in C++ when we make use of private and public base classes.
    // Answers in the next program - or per revealing of trailing comments.
    
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class B {
    private:
      int b;
    public:
      B(int b): b(b){}
      void Bop(){
        cout << "Bop()" << endl;
      }
    
    };
    
    class C {
    private:
      int c;
    public:
      C(int c): c(c){}
      void Cop(){
        cout << "Cop()" << endl;
      }
    };
    
    class D : private B, public C {
    private:
      int d;
    public:
      D(int b, int c, int d): B(b), C(c), d(d){} 
    };
    
    // Reveal...
    int f(D &aD){           
      B *x = new D(1,2,3);  // Error: B is a private base
      C *y = new D(1,2,3);  // OK:    C is a public  base
      D *z = new D(1,2,3);  // OK, of course
      D *v = new B(1);      // Error: Against rules of polymorphism
      D *v = new C(1);      // Error: Against rules of polymorphism
    }
    
    int main(){
      D d(1,2,3);
      f(d);
    }

    Program: Same as above - with answers.
    // Program indended to illustrate and discuss the rules of polymorphism 
    // in C++ when we make use of private and public base classes.
    // Answers in the next program - or per revealing of trailing comments.
    
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class B {
    private:
      int b;
    public:
      B(int b): b(b){}
      void Bop(){
        cout << "Bop()" << endl;
      }
    
    };
    
    class C {
    private:
      int c;
    public:
      C(int c): c(c){}
      void Cop(){
        cout << "Cop()" << endl;
      }
    };
    
    class D : private B, public C {
    private:
      int d;
    public:
      D(int b, int c, int d): B(b), C(c), d(d){} 
    };
    
    // Reveal...
    int f(D &aD){           
      B *x = new D(1,2,3);  // Error: B is a private base
      C *y = new D(1,2,3);  // OK:    C is a public  base
      D *z = new D(1,2,3);  // OK, of course
      D *v = new B(1);      // Error: Against rules of polymorphism
      D *v = new C(1);      // Error: Against rules of polymorphism
    }
    
    int main(){
      D d(1,2,3);
      f(d);
    }

    Exercise 4.4. Friends and 'enemies' of a class with private and public bases

    In this program from the accompanying slide introduce a friend function void frD(D &ad) of class D. (You can just delete the function f).

    From frD access b, c, d in ad. Also from frD call the member functions Bop, Cop, and Dop on ad.

    From an identical non-friend function (let us just call it an enemy) do exactly the same as in FrD.

    Do you get the results you expect?

    Can you arrange that frD can access/call all of b, c, d, Bop, Cop, and Dop?

    Discussion: No single most general class in C++
    Slide Annotated slide Contents Index
    References 

    Reference
    • The C++ Programming Language: Page 438

    There is no Object class - a common superclass to all other classes - in C++?

    How is it possible to do object-oriented programming without class Object?

    Exercise 4.5. No single most general class in C++Discuss the question asked on this slide.


    Collected references
    Contents Index
    About constructors
    The C++ Programming Language: Page 306
    The C++ Programming Language: Page 307
    The C++ Programming Language: Page 402, 849
    Friends
    Base class access control
    The C++ Programming Language: Page 310
    Effective C++, Third edition: Item 7
    Effective C++, Third edition: Item 33
    The C++ Programming Language: Page 313
    The C++ Programming Language: Page 851-852
    Effective C++, Third edition: page 43. Item 7
    The C++ Programming Language: Page 418, 853
    The C++ Programming Language: Page 394
    The C++ Programming Language: Page 396
    Replicated base class
    The C++ Programming Language: Page 405
    The C++ Programming Language: Page 406
    The C++ Programming Language: Page 438

     

    Chapter 4: Abstraction Mechanisms, Part 2
    Course home     Author home     About producing this web     Previous lecture (top)     Next lecture (top)     Previous lecture (bund)     Next lecture (bund)     
    Generated: March 26, 2013, 13:03:41