Chapter 5
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

Inheritance in C++
Slide Contents Index
References 

Overview of the interesting topics related to 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 Contents Index
References 
Intern kommentar til forelæsningen om denne slide:

Programmer med bekvem public state.

Det første program viser hvordan en derived constructor initialiserer med base constructor. De næste to det samme for multipel nedarvning

Endelig to programmer der illustrerer initialiseringsrækkefølge - gå hurtigt over dem

Constructors in class hierarchies work as expected in C++

Reference

Reference
  • The C++ Prog. Lang. (3. edition): Page 306

Reference
  • The C++ Prog. Lang. (4. edition): Page 582

  • 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

    • C++ allows for inheritance of constructors

      • Makes sense if a derived class does not add data members.

Examples next slide

Examples of inheritance: Constructors and destructors
Slide Contents Index
References 

We show examples of constructors and destructors related to inheritance

Program: Constructors and single inheritance - C++11 initialization syntax.
// Single inheritance - warming up - C++11 variant.
// 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. In the next version, there will be 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(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),                  // The Base2 constructor is not activated explictly here.
      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 - base classes are re-ordered.
// Demonstration of construction and destructor order. Bases re-ordered within class Derived.

#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 
}

Program: Attempting to inherit constructors - problems.
// Attempting to inherit constructors - does not compile. C++11.
// Problems in this version. The next version of the program shows a convenient solution.

#include <string>

using namespace std;

class Base{
private:
  int i;
  string s;

public:
  Base(): i{0}, s{string("Hi")}{
  }

  Base(int i): i{i}, s{string("Hi")}{
  }

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

class Derived: public Base{
  // No state in Derived
public:
  Derived() = default;             // Means: We are happy with the default constructor


};

int main(){

  Derived d1,                      // OK
          d2(5),                   // Error: no matching function for call to 'Derived::Derived(int)'
          d3(6, string("AP"));     // Error: no matching function for call to 'Derived::Derived(int, std::string)'
}

Program: Attempting to inherit constructors - success.
// Single inheritance - C++11 variant.
// Illustrates how it is possible to inherit the constructors from Base. C++11.

#include <string>

using namespace std;

class Base{
private:
  int i;
  string s;

public:
  Base(): i{0}, s{string("Hi")}{
  }

  Base(int i): i{i}, s{string("Hi")}{
  }

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

class Derived: public Base{
  // No state in Derived
public:
  using Base::Base;                // A using declaration:
                                   // Inherit the constructors from Base (!!)
                                   // The C++ Programming Language, 4ed, 20.3.5.1 (page 594).
};

int main(){

  Derived d1,                      // OK
          d2(5),                   // OK
          d3(6, string("AP"));     // OK
}

Inheritance: Copying and slicing
Slide Contents Index
References 
Intern kommentar til forelæsningen om denne slide:
Basalt set illustreres slicing. Når et object af Derived type kopieres til en variabel af Base type bliver der slicet. Når objekter håndteres via pointer eller referencer slices der ikke. Dvæl ikke ved det andet program.

'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++ Prog. Lang. (3. edition): Page 307

Reference
  • The C++ Prog. Lang. (4. edition): Page 513

  • 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() override {
     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 is a pointer to aB from main.
  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.
// For the exercise. Class B inherits from A. Illustration of slicing.
// A minor variant of the previous program, where the formal parameter of f is of type A&.  Reveal answer...

#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() override {
     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 5.2. Slicing an object during parameter passing.

In both this the program (and another) 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.

Virtual Functions
Slide Contents Index
References 
Intern kommentar til forelæsningen om denne slide:
Programmet viser hvordan man gør brug af virtuelle funktioner i C++. Kræver pointere eller referencer.

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, §17.2.5 (4ed).

Reference
  • The C++ Prog. Lang. (3. edition): Page 310

Reference
  • The C++ Prog. Lang. (4. edition): Page 585

Reference

  • Conditions for getting polymophic behavior in C++:   obj_ptr->f()

    • A virtual member function f must be called

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

  • Avoiding polymorphic behavior with a virtual function f

    • Activate f with a scope resolution operation: obj_ptr->A::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.

Examles with virtual functions
Slide Contents Index
References 

We show an example that involves virtual functions

Program: Virtual versus non-virtual functions in an AB class hierarchy - relative to illustration on the previous slide.
// 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) override{            // override is a C++11 contextual keyword 
    cout << "virtual vf in B" << endl;
  }

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

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

int f2(A *ap){
  ap->vf(3.0);     // vf in B.
  ap->A::vf(3.0);  // vf in A  - enforce call of vf from A, with use of the scope resolution operator ::
  ap->f(4.0);      // f in A - static binding.

  ap->B::f(4.0);                    // error: 'B' is not a base of 'A'
  dynamic_cast<B*>(ap)->f(4.0);     //f in B - the programmer guarantee that ap is of type B*. 

  cout << endl;
}

int f3(A &ar){
  ar.vf(5.0);      // vf in B
  ar.A::vf(3.0);   // vf in A
  ar.f(6.0);       // f in A
                                    // Same as for pointers:
  ar.B::f(4.0);                     // error: 'B' is not a base of 'A'
  dynamic_cast<B&>(ar).f(4.0);      // f in B.

  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
f in B

virtual vf in B
virtual vf in A
f in A
f in B

  • What to remember when doing OOP with virtual functions in C++:

    • The function in the base class must be virtual

    • The base class function and the derived class functions must have the same name

    • Their parameters must be identical

    • Their return types must be compatible

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

I C++11 it is recommended always to use the (contextual) keyword override to state your intension of overriding a virtual member function

override is a redundant means of expression, intended to cath errors as early as possible

Reference
  • Effective Modern C++: Declare overriding functions override. Item 12

Destructors and Inheritance - Virtual Destructors
Slide Contents Index
References 

A class with virtual functions should always have a virtual destructor

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

Reference
  • The C++ Prog. Lang. (4. edition): Page 488

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.
// The B-object pointed to by a1 is not properly destructed.

#include <iostream>

using namespace std;

class A {
private:
   double a;
public:

  A(double a): a(a){};
  ~A(){cout << "A destructor" << endl;}    // Non-virtual destructor

  // 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 e(){
  cout << "Now start of e" << endl;      
  B b1(6.0);                              // b1 contains a B object
  
  // Work on b1.

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

void f(){
  cout << "Now start of f" << endl;      
  A *a1 = new B(5.0);                     // a1 points to a B object on the free store.

  // Work on a1

  delete a1;                              // The destructor in A called 
                                          // The B destructor is not called. May cause problems (leaking).
                                          // The destructor in A is not virtual.

  cout << "Now end of f" << endl;
}  

int main(){
  e();
  cout << endl << endl;
  f();
}

Program: Program output.
Now start of e
Now end of e
B destructor
A destructor


Now start of f
A destructor 
Now end of f

Program: Base class A and derived class B with virtual destructors.
// Illustration of virtual destructors. Now, the destructor of class A is virtual.

#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);
  
  // Work on a1

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

  cout << "Now end of f" << endl;
}  

int main(){
  f();
}

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

Program: Base class A and derived class B and C with virtual destructors.
// Not much new stuff here. Illustration of virtual destructors in A-B-C class hierarcy. 
// Shows that 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 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

Tricky stuff

Program: A class B inherits two virtual, overloaded functions from A - straightforward - no problems.
// For exercise.
// Class B inherits both overloads of the virtual functions vf.

#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
}

Program: Class B redefines one of the functions, and expects to inherit the other - problems.
// For exercise.
// We override one of the overloads in class B. 
// Is the other - vf(double) -  inherited?

#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:

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

                                            // It is expected that A::vf(double) is inherited.
                                            // BUT THERE ARE PROBLEMS - COMPILER ERROR.
};


int main(){
  B *b = new B();
  
  b->vf();
  b->vf(5.0);                               // Compiler error:  no matching function for call to B::vf(double)
}

Reference

Exercise 5.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 Contents Index
References 
Intern kommentar til forelæsningen om denne slide:
The program shows situations where abstract classes (classes with pure virtual functions) cannot be used. Et godt eksempel.

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

Reference
  • The C++ Prog. Lang. (3. edition): Page 313

Reference
  • The C++ Prog. Lang. (4. edition): Page 597-599

  • 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) override{                    // 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' (as part of parameter passing)
  f2(b2);
  f3(b1);
}

What about interfaces in C++?
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() override{
    return 1;
  }

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

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

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

What about nested classes in C++?
Slide Contents Index
References 
Intern kommentar til forelæsningen om denne slide:
Vær sikker på at forberede eksemplerne ordenligt - uden dette kan det tage lang tid...

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++ Prog. Lang. (3. edition): Page 851-852

Reference
  • The C++ Prog. Lang. (4. edition): Page 469

  • "A member of a nested class can refer to types and static members of its enclosing class"

    • "It can only refer to non-static members when it is given an object of the enclosing class to refer to"

      • Page 469 The C++ Prog. Lang. (4. edition)

  • "A nested class has access to members of its enclosing class, even to private members"

    • "but has no notion of a current object of the enclosing class"

    • Page 469 The C++ Prog. Lang. (4. edition)

  • 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.
    // Inspirred 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"). 
    // 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;                     // OK - Access to types in enclosing class, such as T, is allowed.
      public:
        void fi(Outer *op, int v){
          op->i = v;             // OK - Access to private members in enclosing class via an object (here *op).
                                 // A nested class has access to members of its enclosing class, even to private members 
                                 // (via a pointer to instance of the enclosing class).
    
          op->i2 = v;            // OK - i2 is public.
        }
    
      };  // End of Inner
    
      int fo(Inner* ip){
        ip->fi(this,2);          // OK - Inner::fi is public
        return ip->x;            // error: Inner::x is private. 
                                 // From Outer we cannot access private members in a nested class 
                                 // (via a pointer to an instance of the nested 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 friendship between Outer and Inner classes.
    // Solves visibility problems from earlier version of the program.  Compiles.
    
    #include <iostream>
    #include <string>
    
    class Outer{
    private:
      typedef int T;
      int i;
    public:
      int i2;                                   
      static int s;
    
      class Inner{
        friend class Outer;                     // 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;                                // i is still visible via op
      op->i2 = v;                               
    }
    
    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 - study as an exercise

    Program: A variant where class Inner is private in Outer - does not compile.
    // For exercise.
    // 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{
    private:
      typedef int T;
      int i;
    
      class Inner{              // Inner is now private in Outer
      friend class Outer;       // Outer is still a friend            
      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.
    // For exercise.
    // 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.
    // For exercise.
    // Inspired from page 851 of "The C++ Programming Language", 3ed version.
    // Accessing private_i and public_i as instance variables in Outer via a parameter.
    
    
    #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;       // OK
      int local2 = p->public_i * 3;        // OK
    
      std::cout << local1 << ", " << local2 << std::endl; 
    }
    
    
    int main(){
      Outer o;  
      Outer::Inner i;
    
      i.fi(&o);
    }

    Exercise 5.4. Study the examples of nested classes

    We have provided a number of examples of nested classes: Example 1, example 2, and example 3 on the accompanying slide. Be sure to understand the programs, and explain the findings (as reported in the programs).

    What about prevention of derivation in C++?
    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 has also been introduced in C++11: final

    Reference
    • The C++ Prog. Lang. (4. edition): Page 591-593

    Program: Illustration of how to prevent overriding a virtual function.
    // Illustration of how to prevent overriding of virtual functions in a class A, B, C hierarchy.
    // Virtual B::vf is final. Prevents overriding of vf in class C.
    
    #include <iostream>
    
    using namespace std;
    
    class A {
    private:
       double a;
    public:
      A(double a): a(a){};
    
      void virtual vf(){
        cout << "Virtual vf from A" << endl;
      }
    };
    
    class B : public A {
    private:
      double b;
    public:
      B(double b): A(b-1), b(b){};
    
      void  vf() override final {
        cout << "Virtual vf from B" << endl;
      }
    };
    
    class C : public B {
    private:
      double c;
    public:
      C(double b): B(b-1), c(b){};
    
      void  vf() override{                        // error: overriding final function 'virtual void B::vf()'
        cout << "Virtual vf from C" << endl;
      }
    };
    
    void f(){
      A *a1 = new C(5.0);
      
      a1->vf();
    
      delete a1;
    }  
    
    int main(){
      f();
    }

    Program: Illustration of how to prevent overriding all virtual functions.
    // Illustration of how to prevent overriding of all virtual functions.
    // Class B is final. Makes every virtual function in class B final.  
    
    #include <iostream>
    
    using namespace std;
    
    class A {
    private:
       double a;
    public:
      A(double a): a(a){};
    
      void virtual vf(){
        cout << "Virtual vf from A" << endl;
      }
    };
    
    class B final : public A {
    private:
      double b;
    public:
      B(double b): A(b-1), b(b){};
    
      void  vf() override{
        cout << "Virtual vf from B" << endl;
      }
    };
    
    class C: public B {            // error: cannot derive from 'final' base 'B' in derived type 'C'
    private:
      double c;
    public:
      C(double b): B(b-1), c(b){};
    
      void  vf() override{                       
        cout << "Virtual vf from C" << endl;
      }
    };
    
    void f(){
      A *a1 = new C(5.0);
      
      a1->vf();
    
      delete a1;
    }  
    
    int main(){
      f();
    }

    Pointer to members
    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++ Prog. Lang. (3. edition): Page 418, 853

    Reference
    • The C++ Prog. Lang. (4. edition): Page 607

    Program: Class variant of class Point with several different move functions.
    // Class Point with three member functions of the same signature.
    
    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.
    // Implementation of the member functions. Not really 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 pointers 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

    Multiple inheritance - issues
    Slide Contents Index
    References 

    Let us understand the major reason why multiple inheritance may be 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?

    • Aspects of multiple inheritance

      • Name clash: Does x in C refer to the x in A or the x in B?

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

      • Combination: Can x in A and x in B be combined to a single x in C?

      • Replication: Is there one or two x pieces in C?

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

    Let us understand the major reason why multiple inheritance may be 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

      • Name clash: Does x in C refer to the x in A or the x in B?

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

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

        • Yes - as already seen

      • Combination: 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

      • Replication: Is there one or two x pieces in C?

        • There are two x variables in a C object

    Multiple inheritance: Ambiguities
    Slide Contents Index
    References 
    Intern kommentar til forelæsningen om denne slide:
    Skift gerne frem og tilbage mellem programmerne. Det giver mening og indsigt...

    The scope resolution 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 resolution operator to resolve the ambiguity
    // from a client of class C.
    
    
    #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 AND EXPECTED 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 the classes 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() override {    
         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 Contents Index
    References 

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

    Reference
    • The C++ Prog. Lang. (3. edition): 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

    The default case

    Shared base class: Virtual bases
    Slide Contents Index
    References 

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

    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.

    Per default you get replicated base classes in C++

    Reference
    • The C++ Prog. Lang. (3. edition): Page 396

    Reference
    • The C++ Prog. Lang. (4. edition): Page 632ff

    Reference

    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, (§21.3.5, 4ed)

    A constructor of a virtual base must be called exactly once - see the exercise for additional details

    Exercise 5.5. Ignoring constructors in virtual base class

    Take a look at the class hierarchy on this slide and in this program - in particular how the class D constructor relies on the default constructors in class B and C. Which constructor is used in the A class?

    In this program we show a way out of the problem. Please be sure that you both understand the problem - and the solution.

    Program: Ignoring constructor in virtual base class.
    // For exercise.
    // This program illustrates a slightly surprised construction of the A-part of a D-object.
    // A D object has a B-part and a C-part, in both of which we make use of our own default constructors. Both of these delegates to to A(int).
    // But it turns out that A(int) is NOT used in the construction of a D-object. 
    // Surprise, probably! Please - as an exercise - consider this issue.
    
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class A {
    public:
      int a;
      A(): a(9){}
      A(int a): a(a){}
    };
    
    class B : public virtual A {
    public:
      int b;
      B(): A(5), b(1){}                 // A(int) is ignored in the actual construction of the D-object in main.
    };
    
    class C : public virtual A {
    public:
      int c;
      C(): A(7), c(2){}                 // A(int) is ignored in the actual construction of the D-object in main.
    };
    
    class D : public B, public C {
    public:
      int d;
      D(): d(9){}                       // Use the default constructors of B and C
    };
    
    
    int f(D &x){
      cout << x.a << endl;     // 9     // We se that The default constructor in A have been used.
      cout << x.B::a << endl;  // 9
      cout << x.C::a << endl;  // 9
      cout << x.b << endl;     // 1 
      cout << x.c << endl;     // 2
      cout << x.d << endl;     // 9
    }
    
    int main(){
      D d;
      f(d);
    }

    Program: Forcing use of a constructor in a virtual base class.
    // Solution. Now enforcing use of particular A constructors in the D constructor.
    
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class A {
    public:
      int a;
      A(): a(9){}
      A(int a): a(a){}
    };
    
    class B : public virtual A {
    public:
      int b;
      B(): A(5), b(1){}                 
    };
    
    class C : public virtual A {
    public:
      int c;
      C(): A(7), c(2){}                 
    };
    
    class D : public B, public C {
    public:
      int d;
      D(): C(), B(), A(8), d(9){}       // Notice the initialization of the virtual A object part here: A(8).
    };
    
    
    int f(D &x){
      cout << x.a << endl;     // 8     // The A(int) constructor is forced in the D-constructor.
      cout << x.B::a << endl;  // 8     // See page 634-635 of The C++ Programming Language (4ed).
      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);
    }

    Is repeated inheritance possible?
    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.A::a << endl;
    
      cout << x.d << endl;     
    }
    
    int main(){
      D d;
      f(d);
    }

    Is repeated inheritance possible? No!
    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.

    Reference

    In C++, the answer is no

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

    Member access: private, public and protected
    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++ Prog. Lang. (3. edition): 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

    Base class access: private, public and protected
    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++ Prog. Lang. (3. edition): Page 405

    Reference
    • The C++ Prog. Lang. (4. edition): Page 605

    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 Contents Index
    References 

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

    Reference
    • The C++ Prog. Lang. (3. edition): Page 405

    Reference
    • The C++ Prog. Lang. (4. edition): Page 605

    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 (when A is a private base of B) 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 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++ Prog. Lang. (3. edition): Page 406

    Reference
    • The C++ Prog. Lang. (4. edition): Page 605

    • 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++ Prog. Lang. (4. edition) formulations of the rules page 605

    Base class access - Examples
    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 (with multiple inheritance).
    // 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){} 
    };
    
    // Which are legal? We care about the static types of x, y, z, v, and w. Reveal...
    int f(){           
      B *x = new D(1,2,3);  // Error: B is a private base. The D-object is not a B-object.
      C *y = new D(1,2,3);  // OK:    C is a public  base. The D-object is a C-object.
      D *z = new D(1,2,3);  // OK, of course.
      D *v = new B(1);      // Error: Against rules of polymorphism.
      D *w = new C(1);      // Error: Against rules of polymorphism.
    }
    
    int main(){
      f();
    }

    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 (with multiple inheritance).
    // 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){} 
    };
    
    // Which are legal? We care about the static types of x, y, z, v, and w. Reveal...
    int f(){           
      B *x = new D(1,2,3);  // Error: B is a private base. The D-object is not a B-object.
      C *y = new D(1,2,3);  // OK:    C is a public  base. The D-object is a C-object.
      D *z = new D(1,2,3);  // OK, of course.
      D *v = new B(1);      // Error: Against rules of polymorphism.
      D *w = new C(1);      // Error: Against rules of polymorphism.
    }
    
    int main(){
      f();
    }

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

    In this program from the accompanying slide arrange that the function void frD(D &ad) becomes a friend of class D. (You can just rename the function f to frD and arrange the friendship).

    From frD , please access b, c, d in ad. Also from frD , please also 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 (and the errors) 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 Contents Index
    References 

    Reference
    • The C++ Prog. Lang. (3. edition): 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 5.7. No single most general class in C++Discuss the question asked on this slide.


    Templates

    Templates - overview
    Slide Contents Index
    References 

    Templates in C++ provide for compile-time parametrization of functions and classes

    Templates correspond to generic types and generic methods in Java and C#

    Reference
    • The C++ Prog. Lang. (4. edition): Page 668ff.

    • Template parameters

      • Types

      • Values of template types

      • Constants of integer types

      • Pointer to an object, a pointer to function, or pointer to a member

      • It is not possible to pass, doubles, floats, or string constants as a template arguments

    • Template specialization

      • It is possible to define several specializations (variants) of a template distinguished by a 'formal type parameter pattern'

      • Can, for instance, be used to provide a shared template for all pointer types

      • Both for class and function templates

    The C++ template facility is macros that look like classes [Anders Hejlsberg].

    Templates versus generics in Java, C# and C++
    Slide Contents Index
    References 

    Templates in C++ can be compared with generics in Java and C#

    • Java

      • Type erasure: type parameters are compiled away.

        • Problems with reflection - cannot reestablish actual type parameters

    • C#

      • Reflection with generic types is possible

      • Support of constraints on formal parameter types: where clauses

        • where class;   where struct;   where new();   where C;   where C, I

      • When value types are used as actual type parameters

        • Separate instantiations are generated

    • C++

      • Template specialization, and partial specialization

        • Allow alternative implementation for certain template parameters

        • To avoid code bloat in some situations

          • Containers of pointers share their implementation     (§25.3, 4ed)

      • Powerful compile time calculations (metaprogramming) - Turing complete

      • Templates cannot be compiled as such     (§23.3.2, 4ed)

        • The instantiations are compiled

        • Only relatively superficial compiler check of the template - before any instantiation

      • Relies on compile-time duck typing

    Class templates in C++
    Slide Contents Index
    References 

    In many ways similar to generic classes in C# and Java

    • Template classes versus classes

      • A template can be used to create classes by means of template instantiation

    • Template instantiation

      • Supplying actual template parameters

      • Template parameters to classes must be supplied explicitly

      • Each instantiation creates a class which has the same status as a hand crafted class

    • "The generated classes and functions are perfectly ordinary classes that obey all the usual rules for classes"

      • "... a powerful way of generating code"

      • "... a certain amount of caution is in order to avoid flooding memory with almost identical [...] definitions"

      • The C++ Prog. Lang. (4. edition) page 671.

    • Type parameters are not related by inheritance

      • "There are no requirements that different arguments for the same template parameter should be related by inheritance", The C++ Prog. Lang. (4. edition) page 671.

      • No constraints on formal type parameters as in C#

    Example - Point<C>
    Slide Contents Index
    References 

    We make a type parameterized Point - class Point<C>

    A modest first example...

    Program: A type parameterized variant of class Point - point.h.
    // point.h
    
    template<typename C>class Point {
    private: 
      C x, y;
    
    public:
      Point(C, C);
      Point();
      C getx () const;
      C gety () const;
      Point<C>& move(C, C);
      double distance_to(Point);
    };
    
    template<class C>  std::ostream& operator<< (std::ostream&, const Point<C>&);

    Program: The implementation of the template class Point - point.cc.
    // point.cc
    
    #include <cmath>
    #include <iostream>
    #include "point.h"
    
    template<typename C> Point<C>::Point(C x_coord, C y_coord):
       x(x_coord), y(y_coord){
    }
    
    template<typename C> Point<C>::Point(): x(0.0), y(0.0){
    }
    
    template<typename C> C Point<C>::getx () const{
      return x;
    }
    
    template<typename C> C Point<C>::gety () const{
      return y;
    }
    
    template<typename C> Point<C>& Point<C>::move(C dx, C dy){
      x += dx; y += dy;                                            // Implicitly assume that + applies to C values 
      return *this;
    }
    
    template<typename C> double Point<C>::distance_to(Point<C> p){
      return sqrt((x - p.x) * (x - p.x) + (y - p.y) * (y - p.y));  // Similar assumptions for - and *
    }
    
    
    template<typename C> std::ostream& operator<< 
                            (std::ostream& s, const Point<C>& p){
      return s << "(" << p.getx() << "," << p.gety() << ")" ;
    }

    Program: A program that illustrate template instantiation.
    #include <iostream>
    #include <string>
    #include "point.cc"  // Notice inclusion of the cc file, in order to 
                         // instantiate the template for int and double.
                         // Else lots of linker errors will occur.
                         // This treats templates in the same way as inline functions.
                         // The C++ Programming Language, 4ed, page 695ff.
    
    int main(){
      Point<double> pd1,
                    pd2(1,2);
    
      Point<int>    pi1,
                    pi2(3,4);
    
      pd2.move(1,1);
      pi2.move(1,1),
    
      std::cout << "pd2: " << pd2 << std::endl;      // (2,3)
      std::cout << "pi2: " << pi2 << std::endl;      // (4,5)
    }

    Program: A program that illustrate problematic template instantiation.
    #include <iostream>
    #include <string>
    #include "point.cc"  // Notice inclusion of the cc file, in order to 
                         // instantiate the template for int and double.
                         // Else lots of linker errors will occur.
                         // This treats templates in the same way as inline functions.
                         // The C++ Programming Language, 4ed, page 695ff.
    
    int main(){
      Point<double> pd1,
                    pd2(1,2);
    
      Point<int>    pi1,
                    pi2(3,4);
    
      pd2.move(1,1);
      pi2.move(1,1),
    
      std::cout << "pd2: " << pd2 << std::endl;      // (2,3)
      std::cout << "pi2: " << pi2 << std::endl;      // (4,5)
    
      string s{};
      Point<std::string> ps1;             // Gives rise to A LOT of complex error messages
                                          // Point<string> is compiled, and there is a lot of problems in this class.
                                          // Not necessarily meaningful...
    
      ps1.move(string("a"), string("b")); // Maybe a challenge...
    
    }

    Program: Compiler errors for the program from above - typical for compilation of template instances with errors.
    $ g++ prog1.cc point.o -std=c++11
    prog1.cc: In function ‘int main()’:
    prog1.cc:22:3: error: ‘string’ was not declared in this scope
       string s{};
       ^
    prog1.cc:22:3: note: suggested alternative:
    In file included from /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/iosfwd:39:0,
                     from /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/ios:38,
                     from /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/ostream:38,
                     from /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/iostream:39,
                     from prog1.cc:1:
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/stringfwd.h:62:33: note:   ‘std::string’
       typedef basic_string<char>    string;
                                     ^
    In file included from prog1.cc:3:0:
    point.cc: In instantiation of ‘Point<C>::Point() [with C = std::basic_string<char>]’:
    prog1.cc:23:22:   required from here
    point.cc:11:54: error: no matching function for call to ‘std::basic_string<char>::basic_string(double)’
     template<typename C> Point<C>::Point(): x(0.0), y(0.0){
                                                          ^
    point.cc:11:54: note: candidates are:
    In file included from /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/string:52:0,
                     from /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/locale_classes.h:40,
                     from /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/ios_base.h:41,
                     from /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/ios:42,
                     from /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/ostream:38,
                     from /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/iostream:39,
                     from prog1.cc:1:
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:540:9: note: template<class _InputIterator> std::basic_string<_CharT, _Traits, _Alloc>::basic_string(_InputIterator, _InputIterator, const _Alloc&)
             basic_string(_InputIterator __beg, _InputIterator __end,
             ^
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:540:9: note:   template argument deduction/substitution failed:
    In file included from prog1.cc:3:0:
    point.cc:11:54: note:   candidate expects 3 arguments, 1 provided
     template<typename C> Point<C>::Point(): x(0.0), y(0.0){
                                                          ^
    In file included from /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/string:52:0,
                     from /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/locale_classes.h:40,
                     from /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/ios_base.h:41,
                     from /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/ios:42,
                     from /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/ostream:38,
                     from /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/iostream:39,
                     from prog1.cc:1:
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:530:7: note: std::basic_string<_CharT, _Traits, _Alloc>::basic_string(std::initializer_list<_Tp>, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
           basic_string(initializer_list<_CharT> __l, const _Alloc& __a = _Alloc());
           ^
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:530:7: note:   no known conversion for argument 1 from ‘double’ to ‘std::initializer_list<char>’
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:512:7: note: std::basic_string<_CharT, _Traits, _Alloc>::basic_string(std::basic_string<_CharT, _Traits, _Alloc>&&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
           basic_string(basic_string&& __str)
           ^
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:512:7: note:   no known conversion for argument 1 from ‘double’ to ‘std::basic_string<char>&&’
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:502:7: note: std::basic_string<_CharT, _Traits, _Alloc>::basic_string(std::basic_string<_CharT, _Traits, _Alloc>::size_type, _CharT, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; std::basic_string<_CharT, _Traits, _Alloc>::size_type = unsigned int]
           basic_string(size_type __n, _CharT __c, const _Alloc& __a = _Alloc());
           ^
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:502:7: note:   candidate expects 3 arguments, 1 provided
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:495:7: note: std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
           basic_string(const _CharT* __s, const _Alloc& __a = _Alloc());
           ^
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:495:7: note:   no known conversion for argument 1 from ‘double’ to ‘const char*’
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:488:7: note: std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, std::basic_string<_CharT, _Traits, _Alloc>::size_type, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; std::basic_string<_CharT, _Traits, _Alloc>::size_type = unsigned int]
           basic_string(const _CharT* __s, size_type __n,
           ^
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:488:7: note:   candidate expects 3 arguments, 1 provided
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:476:7: note: std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const std::basic_string<_CharT, _Traits, _Alloc>&, std::basic_string<_CharT, _Traits, _Alloc>::size_type, std::basic_string<_CharT, _Traits, _Alloc>::size_type, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; std::basic_string<_CharT, _Traits, _Alloc>::size_type = unsigned int]
           basic_string(const basic_string& __str, size_type __pos,
           ^
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:476:7: note:   candidate expects 4 arguments, 1 provided
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:467:7: note: std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const std::basic_string<_CharT, _Traits, _Alloc>&, std::basic_string<_CharT, _Traits, _Alloc>::size_type, std::basic_string<_CharT, _Traits, _Alloc>::size_type) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; std::basic_string<_CharT, _Traits, _Alloc>::size_type = unsigned int]
           basic_string(const basic_string& __str, size_type __pos,
           ^
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:467:7: note:   candidate expects 3 arguments, 1 provided
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:460:7: note: std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const std::basic_string<_CharT, _Traits, _Alloc>&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
           basic_string(const basic_string& __str);
           ^
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:460:7: note:   no known conversion for argument 1 from ‘double’ to ‘const std::basic_string<char>&’
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:453:7: note: std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
           basic_string(const _Alloc& __a);
           ^
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:453:7: note:   no known conversion for argument 1 from ‘double’ to ‘const std::allocator<char>&’
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:442:7: note: std::basic_string<_CharT, _Traits, _Alloc>::basic_string() [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
           basic_string()
           ^
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:442:7: note:   candidate expects 0 arguments, 1 provided
    In file included from prog1.cc:3:0:
    point.cc:11:54: error: no matching function for call to ‘std::basic_string<char>::basic_string(double)’
     template<typename C> Point<C>::Point(): x(0.0), y(0.0){
                                                          ^
    point.cc:11:54: note: candidates are:
    In file included from /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/string:52:0,
                     from /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/locale_classes.h:40,
                     from /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/ios_base.h:41,
                     from /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/ios:42,
                     from /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/ostream:38,
                     from /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/iostream:39,
                     from prog1.cc:1:
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:540:9: note: template<class _InputIterator> std::basic_string<_CharT, _Traits, _Alloc>::basic_string(_InputIterator, _InputIterator, const _Alloc&)
             basic_string(_InputIterator __beg, _InputIterator __end,
             ^
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:540:9: note:   template argument deduction/substitution failed:
    In file included from prog1.cc:3:0:
    point.cc:11:54: note:   candidate expects 3 arguments, 1 provided
     template<typename C> Point<C>::Point(): x(0.0), y(0.0){
                                                          ^
    In file included from /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/string:52:0,
                     from /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/locale_classes.h:40,
                     from /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/ios_base.h:41,
                     from /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/ios:42,
                     from /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/ostream:38,
                     from /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/iostream:39,
                     from prog1.cc:1:
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:530:7: note: std::basic_string<_CharT, _Traits, _Alloc>::basic_string(std::initializer_list<_Tp>, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
           basic_string(initializer_list<_CharT> __l, const _Alloc& __a = _Alloc());
           ^
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:530:7: note:   no known conversion for argument 1 from ‘double’ to ‘std::initializer_list<char>’
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:512:7: note: std::basic_string<_CharT, _Traits, _Alloc>::basic_string(std::basic_string<_CharT, _Traits, _Alloc>&&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
           basic_string(basic_string&& __str)
           ^
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:512:7: note:   no known conversion for argument 1 from ‘double’ to ‘std::basic_string<char>&&’
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:502:7: note: std::basic_string<_CharT, _Traits, _Alloc>::basic_string(std::basic_string<_CharT, _Traits, _Alloc>::size_type, _CharT, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; std::basic_string<_CharT, _Traits, _Alloc>::size_type = unsigned int]
           basic_string(size_type __n, _CharT __c, const _Alloc& __a = _Alloc());
           ^
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:502:7: note:   candidate expects 3 arguments, 1 provided
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:495:7: note: std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
           basic_string(const _CharT* __s, const _Alloc& __a = _Alloc());
           ^
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:495:7: note:   no known conversion for argument 1 from ‘double’ to ‘const char*’
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:488:7: note: std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, std::basic_string<_CharT, _Traits, _Alloc>::size_type, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; std::basic_string<_CharT, _Traits, _Alloc>::size_type = unsigned int]
           basic_string(const _CharT* __s, size_type __n,
           ^
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:488:7: note:   candidate expects 3 arguments, 1 provided
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:476:7: note: std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const std::basic_string<_CharT, _Traits, _Alloc>&, std::basic_string<_CharT, _Traits, _Alloc>::size_type, std::basic_string<_CharT, _Traits, _Alloc>::size_type, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; std::basic_string<_CharT, _Traits, _Alloc>::size_type = unsigned int]
           basic_string(const basic_string& __str, size_type __pos,
           ^
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:476:7: note:   candidate expects 4 arguments, 1 provided
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:467:7: note: std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const std::basic_string<_CharT, _Traits, _Alloc>&, std::basic_string<_CharT, _Traits, _Alloc>::size_type, std::basic_string<_CharT, _Traits, _Alloc>::size_type) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; std::basic_string<_CharT, _Traits, _Alloc>::size_type = unsigned int]
           basic_string(const basic_string& __str, size_type __pos,
           ^
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:467:7: note:   candidate expects 3 arguments, 1 provided
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:460:7: note: std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const std::basic_string<_CharT, _Traits, _Alloc>&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
           basic_string(const basic_string& __str);
           ^
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:460:7: note:   no known conversion for argument 1 from ‘double’ to ‘const std::basic_string<char>&’
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:453:7: note: std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
           basic_string(const _Alloc& __a);
           ^
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:453:7: note:   no known conversion for argument 1 from ‘double’ to ‘const std::allocator<char>&’
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:442:7: note: std::basic_string<_CharT, _Traits, _Alloc>::basic_string() [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
           basic_string()
           ^
    /usr/lib/gcc/i686-pc-cygwin/4.9.2/include/c++/bits/basic_string.h:442:7: note:   candidate expects 0 arguments, 1 provided
    $

    • Lessons learned

      • The definition of member functions in the template class becomes more complex

        • See the file point.cc

      • Template instantiation first occurs when Point<int>, Point<double> and Point<string> is encountered

        • For this reason, point.cc must be included in prog.cc instead of (as usual) point.h

      • Compiler error messages from instances of templates are difficult to deal with

        • It is attractive to provide error messages at a higher level

    Exercise 5.8. A template class with friendsArrange that operator<< becomes a friend of the template class Point<C>. It may be useful to consult §C.13.2, page 854-855 in The C++ Prog. Lang. (3. edition). Or Section 23.4.7 in The C++ Prog. Lang. (4. edition), page 682-684.

    Example - Point<C,dim,default_value>
    Slide Contents Index
    References 

    We make a Point class parameterized with a type C, an integer dimension, and some default value in C

    All template parameters are defaulted

    Program: A type parameterized variant of class Point - point.h.
    // Multi dimensional point. The dimension and a default value is supplied as a template parameter.
    // Defaults are provided for all three template parameters.
    
    #include <vector>
    
    template<typename C, int dim, C def> class Point;
    
    template<typename C, int dim, C def> std::ostream& operator<<
                                 (std::ostream& s, const Point<C,dim,def>& p);
    
    class PointDimProblem{};
    
    
    template<typename C = int, int dim = 3, C def = 0>class Point {
    private: 
      C values[dim];            // An array of C values
    
    public:
      Point();
      C get (int i) const;
      Point<C, dim, def>& move(const std::vector<int>&);
      double distance_to(Point<C, dim, def>);
      friend  std::ostream& operator<< <>
                                 (std::ostream&, const Point<C, dim, def>&);
    };

    Program: The implementation of the template class Point - point.cc.
    // The implementation of the Point template class.
    
    #include <cmath>
    #include <iostream>
    #include "point.h"
    
    // Default contructor
    template<typename C, int dim, C def> Point<C, dim, def>::Point() {
      for (int i = 0; i < dim; i++)
        values[i] = def;
    }
    
    // The get method access dimension i of the point
    template<typename C, int dim, C def> C Point<C, dim, def>::get(int i) const{
      if (i >= 1 && i <= dim)
        return values[i-1];
      else 
        throw PointDimProblem();
    }
    
    // Move the point by means of a vector of int.
    template<typename C, int dim, C def> Point<C, dim, def>&    // deltas is a vector<int>
                              Point<C, dim, def>::move(const std::vector<int>& deltas){  
      for (int i = 0; i < dim; i++)                                               
        values[i] += deltas[i];
      return *this;
    }
    
    // Distance to method - Using some funny norm:
    template<typename C, int dim, C def> double Point<C, dim, def>::distance_to(Point p){    
      double temp = 0.0;
      for (int i = 0; i < dim; i++)
        temp += std::abs(values[i] - p.values[i]);
    
      return temp;
    }
    
    
    // The << operator for points.
    template<typename C, int dim, C def> std::ostream& operator<<
                              (std::ostream& s, const Point<C, dim, def>& p){
      s << "(";
      for(int i = 0; i < dim-1; i++)
        s << p.values[i] << ", ";
      s << p.values[dim-1];
      s << ")" << std::endl;
    
      return s;
    }

    Program: A program that illustrate the template instantiation.
    // A program that illustrates how to use the parameterized Point template class
    
    #include <iostream>
    #include <string>
    #include <vector>
    #include "point.cc"           // Inclusion of the .cc (.cpp) file, not just .h file
    
    const int Dim = 10;
    
    int main(){
      using namespace std;
    
      Point<long int> pli1;       // Default dimension and default default value.
    
      Point<int,Dim,7> pi1, pi2;  // double is illegal as type parameter in this context,
                                  // because non-integer constants are not allowed!
      Point<char, 4, 65> pc1;
      Point<> pi3;                // All three parameters are defaulted.
    
      // Diplacement int vectors:
      vector<int> displacement1 {1, 2, 3};
    
      vector<int> displacement2;
      for(int i = 0; i < Dim; i++) displacement2.push_back(i*2);
    
      // Move points
      pli1.move(displacement1);
      pi1.move(displacement2);
      pc1.move(displacement2);
      pi3.move(displacement1);
    
      // Print points:
      cout << "pli1: " << pli1 << std::endl;     // (1, 2, 3)
      cout << "pi1: "  << pi1 << std::endl;      // (7, 9, 11, 13, 15, 17, 19, 21, 23, 25)
      cout << "pc1: "  << pc1 << std::endl;      // (A, C, E, G)
      cout << "pi3: "  << pi3 << std::endl;      // (1, 2, 3)
    
      cout << "|pi1 - pi2| = " << pi1.distance_to(pi2) << endl;  // 90
    }

    Program: Program output.
    pli1: (1, 2, 3)
    
    pi1: (7, 9, 11, 13, 15, 17, 19, 21, 23, 25)
    
    pc1: (A, C, E, G)
    
    pi3: (1, 2, 3)
    
    |pi1 - pi2| = 90

    • Lessons learned

      • It is only possible to pass template constant parameters of integer type

        • The compiler disallows a constant of type double

      • The details in point.cc becomes even more challenging

      • Tricky to deal with the overloading of operator<< as a friend and template function

        • The compiler helps a bit...

    Function templates
    Slide Contents Index
    References 

    Functions outside classes can also be defined as templates

    Reference

    Program: The template function compare, and various uses of it.
    // The template function compare, and a number of applications (most are OK, some lead to compile time errors).
    
    #include <iostream>
    #include <string>
    #include "point.h"
    
    class ComparsionProblem{};
    
    template<typename T> int compare(const T &v1, const T &v2){
      if (v1 < v2) 
        return -1;
      else if (!(v1 < v2) && !(v2 < v1))  // better than v1 == v2
        return 0;
      else if (v2 < v1)                   // better than v1 > v2
        return 1;
      else 
        throw ComparsionProblem();
    }
    
    int main(){
      using namespace std;
    
      // Comparing int, infers the type automatically:
      cout << compare(1,2) << endl;             // -1
      cout << compare(1,1) << endl;             //  0
      cout << compare(2,1) << endl;             //  1
      cout << endl;
    
      // Passing type parameteter explicitly:
      cout << compare<int>(1,2) << endl;        // -1
      cout << compare<int>(1,1) << endl;        //  0
      cout << compare<int>(2,1) << endl;        //  1
      cout << endl;
    
      string s1 = "abe",
             s2 = "katten",
             s3 = "abe";
    
      // Comparing strings:
      cout << compare(s1,s2) << endl;           // -1
      cout << compare(s2,s1) << endl;           //  1
      cout << compare(s1,s3) << endl << endl;   //  0
      cout << endl;
    
      // Comparing C-style strings:
      cout << compare("ABE","KAT") << endl;     //  1. Wrong. Why? Because we compare pointers...
      cout << compare("KAT","ABE") << endl;     // -1. Wrong.
      cout << compare("kat","katten") << endl;  // error: no matching function for call to 
                                                // compare(const char [4], const char [7])
    
      // Comparing Points (without having defined < on Point):
      Point p1(1,2), p2(3,4);                   
      int i = compare(p1, p2);                  // error: no match for operator< in v2 < v1
                                                // error: no match for operator< in v1 < v2
                                                // error: no match for operator< in v2 < v1
    
    }

    We make a version of class Point with an overloaded < operator

    Program: Class Point with comparison operator friends.
    // Preparing for definition of operator< in class Point.
    
    class Point {
    private: 
      double x, y;
    
    public:
      Point(double, double);
      Point();
      double getx () const;
      double gety () const;
      void move(double, double);
      double distance_to(Point);
    
      friend bool operator<(const Point&, const Point&);
    };
    
    std::ostream& operator<<(std::ostream&, const Point&);

    Program: The implementation of class Point.
    // Implementation of Point members, most interesting operator< at the bottom of the file.
    
    #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(double dx, double dy){
        x += dx; 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() << ")" ;
    }
    
    // Implementation of comparisons that only take the x-coordinate into account.
    
    bool operator<(const Point& p1, const Point& p2){
      return (p1.x < p2.x);
    }

    Program: The template function compare used for Point comparison.
    // An excerpt of the previously shown program that uses the compare template function.
    // Here we show the use of compare on instances of class Point.
    
    #include <iostream>
    #include <string>
    #include "point.h"
    
    class ComparsionProblem{};
    
    template<typename T> int compare(const T &v1, const T &v2){
      if (v1 < v2) 
        return -1;
      else if (!(v1 < v2) && !(v2 < v1)) 
        return 0;
      else if (v2 < v1)                  
        return 1;
      else 
        throw ComparsionProblem();
    }
    
    int main(){
      using namespace std;
    
    
      Point p1(1,2), p2(3,4);                   
      cout << compare(p1, p1) << endl;          //  0
      cout << compare(p1, p2) << endl;          // -1
      cout << compare(p2, p1) << endl;          //  1
    }

    Inappropriate actual type parameters are simply discovered by the compiler

    Appropriateness is not expressed by inheritance relationships

    Type parameter may be deduced automatically in function templates

    Reference
    • Effective Modern C++: template type deduction. Item 1

    Policies with Templates
    Slide Contents Index
    References 

    A type parameter can be used to parameterize a class or function with a class that represents a given policy

    For instance a set of functions that compare data in a certain way

    Reference
    • The C++ Prog. Lang. (3. edition): Page 338

    Reference
    • The C++ Prog. Lang. (4. edition): Page 930

    • Why use templates for policies

      • As an alternative to a function parameter, or to an instance of a class with virtual functions

      • Use of templates will typically give more efficient programs

        • Compile time substitutions

    See details in §13.4 in The C++ Prog. Lang. (3. edition)

    Example of Policies with Templates
    Slide Contents Index
    References 

    We parameterize class Point with a 'policy for distance and comparison'

    Program: Class Point parameterized with a policy class.
    // Ilustration of class point parameterized with different policy types.
    // A bundle of (related) functions passed to Point (and all members of Point) at compile time.
    
    #include <iostream>
    #include <string>
    #include "point.cc"
    #include "norms.cc"
    
    template<typename C>void do_tell_about(C x, C y){
      double dist = x.distance_to(y);
      std::cout << "Distance: " << dist << std::endl; 
      std::cout << "Less than? " << (x.less_than(y) ? "true" : "false") << std::endl << std::endl;
    }
    
    int main(){
      using namespace std;
    
      double dist;
      Point<FixedNorm> p1(1,2),          // Here we parameterize p1 and p2 with a bundle of functions from Fixed form. 
                       p2(4,11);
      do_tell_about(p1,p2);
    
      Point<HorizontalNorm> p3(1,2),     // Ditto p3 and p4 with HorizontalNorm
                            p4(4,11);
      do_tell_about(p3,p4);
    
      Point<VerticalNorm> p5(1,2),       // Ditto p5 and p6 with VerticalNorm
                          p6(4,11);
      do_tell_about(p5,p6);
    
      Point<Norm> p7(1,2),               // Ditto p7 and p8 with Norm
                  p8(4,11);
      do_tell_about(p7,p8);
    }

    Program: Program output.
    Distance: 7
    Less than? false
    
    Distance: 3
    Less than? true
    
    Distance: 9
    Less than? true
    
    Distance: 9.48683
    Less than? true

    Program: Four different policy classes - with type parameterized static methods.
    // Definition of four different template classes.
    // Each defining static member template functions less_than and distance_to.
    // Notice that we just assume that getx and gety are public members in C!
    // Other designs may be possible - and preferable!
    
    #include <cmath>
    
    struct FixedNorm{
      template<typename C> static bool less_than(const C& a, const C& b){
        return false;
      }
    
      template<typename C> static double distance_to(const C& a, const C& b){
        return 7.0;
      }
    };
    
    
    
    struct HorizontalNorm{
      template<typename C> static bool less_than(const C& a, const C& b){
        return a.getx() < b.getx();
      }
    
      template<typename C> static double distance_to(const C& a, const C& b){
        return fabs(a.getx() - b.getx());
      }
    };
    
    
    
    struct VerticalNorm{
      template<typename C> static bool less_than(const C& a, const C& b){
        return a.gety() < b.gety();
      }
    
      template<typename C> static double distance_to(const C& a, const C& b){
        return fabs(a.gety() - b.gety());
      }
    };
    
    
    
    template<typename T>double square(T a){
      return a*a;
    }
    
    struct Norm{
      template<typename C> static bool less_than(const C& a, const C& b){
        return (a.getx() < b.getx()) && (a.gety() < b.gety());
      }
    
      template<typename C> static double distance_to(const C& a, const C& b){
        return sqrt(square(a.getx() - b.getx()) + square(a.gety() - b.gety()));
      }
    };

    Program: The Point class template definition - policy parameterized.
    // Class Point parameterized with a policy type P in which distances and comparisons are supplied.
    
    template <typename P> class Point {
    private: 
      double x, y;
    
    public:
      Point(double, double);
      Point();
      double getx () const;
      double gety () const;
    
      double distance_to(Point<P>);
      bool less_than(const Point<P>&);
    };
    
    template <typename P> std::ostream& operator<<(std::ostream&, const Point<P>&);

    Program: Member functions in class Point.
    // Definition of members in template class Point. Notice the use of static members from the type P.
    
    #include <cmath>
    #include <iostream>
    #include "point.h"
    
    template<typename P>Point<P>::Point(double x_coord, double y_coord): x(x_coord), y(y_coord){
    }
    
    template<typename P>Point<P>::Point(): x(0.0), y(0.0){
    }
    
    template<typename P> double Point<P>::getx () const{
      return x;
    }
    
    template<typename P> double Point<P>::gety () const{
      return y;
    }
    
    template<typename P> double Point<P>::distance_to(Point<P> p){
        return P::distance_to(*this,p);
    }
    
    template<typename P> bool Point<P>::less_than(const Point<P>& p){
        return P::less_than(*this,p);
    }
    
    template<typename P> std::ostream& operator<<(std::ostream& s, const Point<P>& p){
      return s << "(" << p.getx() << "," << p.gety() << ")" ;
    }

    Below we parameterize individual functions with a policy

    Program: Two Point functions parameterized with a policy class.
    // A new example.
    // In this file we parameterize individual Point functions with a policy.
    // Compile time parametrization of functions - an alternative to passing function objects at run time.
    
    #include <iostream>
    #include <string>
    #include "point.h"
    #include "norms.cc"
    
    // distance_to and less_than as template functions: 
    template<typename N> double distance_to(const Point& p, const Point& q){
      return N::distance_to(p,q);
    }
    
    template<typename N> bool less_than(const Point& p, const Point& q){
        return N::less_than(p,q);
    }
    
    int main(){
      using namespace std;
      double dist;
    
      Point p1(1,2),
            p2(4,11);
      dist = distance_to<HorizontalNorm>(p1,p2);                       // Parameterizing distance_to with a
      cout << "Distance: " << dist << endl;                            // bundle of two functions from HorizontalNorm
      cout << "Less than? " << 
           (less_than<HorizontalNorm>(p1,p2) ? "true" : "false") <<    // Ditto less_than
           endl << endl;   
    
      Point p3(1,2),                               
            p4(4,11);
      dist = distance_to<Norm>(p1,p2);                                 // Ditto - here parameterizing with Norm
      cout << "Distance: " << dist << endl; 
      cout << "Less than? " << (less_than<Norm>(p1,p2) ? "true" : "false") << endl << endl;
    
    }

    Program: Program output.
    Distance: 3
    Less than? true
    
    Distance: 9.48683
    Less than? true

    Program: Four different policy classes - with type parameterized static methods.
    // Four different Point policies - the static methods are now specific to class Point.
    // This file does not have any template classes nor template functions.
    
    #include <cmath>
    #include "Point.h"
    
    struct FixedNorm{
      static bool less_than(const Point& a, const Point& b){
        return false;
      }
    
      static double distance_to(const Point& a, const Point& b){
        return 7.0;
      }
    };
    
    
    struct HorizontalNorm{
      static bool less_than(const Point& a, const Point& b){
        return a.getx() < b.getx();
      }
    
      static double distance_to(const Point& a, const Point& b){
        return fabs(a.getx() - b.getx());
      }
    };
    
    
    struct VerticalNorm{
      static bool less_than(const Point& a, const Point& b){
        return a.gety() < b.gety();
      }
    
      static double distance_to(const Point& a, const Point& b){
        return fabs(a.gety() - b.gety());
      }
    };
    
    
    template<typename T>double square(T a){
      return a*a;
    }
    
    struct Norm{
     static bool less_than(const Point& a, const Point& b){
        return (a.getx() < b.getx()) && (a.gety() < b.gety());
      }
    
     static double distance_to(const Point& a, const Point& b){
        return sqrt(square(a.getx() - b.getx()) + square(a.gety() - b.gety()));
      }
    };

    Program: The Point class definition - not a template in this version.
    // Another variant of class Point - nothing of particular interest.
    
    #ifndef POINT_H
    #define POINT_H
    
    #include <iostream>
    
    
    class Point {
    private: 
      double x, y;
    
    public:
      Point(double, double);
      Point();
      double getx () const;
      double gety () const;
    
    };
    
    std::ostream& operator<<(std::ostream&, const Point&);
    
    #endif // POINT_H

    Program: Member functions in class Point - not essential to this example.
    // Point member definitions. Nothing of particularly interest.
    
    #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;
    }
    
    
    std::ostream& operator<<(std::ostream& s, const Point& p){
      return s << "(" << p.getx() << "," << p.gety() << ")" ;
    }

    Template Specialization
    Slide Contents Index
    References 

    It is possible to provide a special version of a template for a particular type of parameter

    Template specialization can be used for both class templates and function templates

    Reference
    • The C++ Prog. Lang. (3. edition): Page 341

    Reference
    • The C++ Prog. Lang. (4. edition): Page 721

    • Class template specialization

      • Can be used to provide a single and shared implementation for all pointer types

      • Example: vector<C> is specialized to vector<C*>

      • Details in §25.3 in The C++ Prog. Lang. (4. edition)

    Reference

    Function templates can also be specialized

    Example - Specialization of Point<C> to Point<char>
    Slide Contents Index
    References 

    We show how to specialize Point<C> to Point<char>

    This example solely demonstrates some technicalities of class template specialization

    This is example is not useful in its own right

    Program: The general class template Point followed by the specialized one.
    // First the general Point template class.
    
    template<typename C> class Point;
    template<typename C> std::ostream& operator<<(std::ostream& s, const Point<C>& p);
    
    template<typename C>class Point {
    private: 
      C x, y;
    
    public:
      Point(C, C);
      Point();
      C getx () const;
      C gety () const;
      Point<C>& move(C, C);
      double distance_to(Point<C>);
      friend  std::ostream& operator<< <>(std::ostream&, const Point<C>&);
    };
    
    // A funny complete, specialization for Point<char>. All details are inlined in this version.
    template<>class Point <char>{
    private: 
      char x, y;
    public:
      Point(char c1, char c2): x(c1), y(c2) {}
      Point(): x('a'), y('z'){}
      char getx () const {return x;}
      char gety () const {return y;}
      Point<char>& move(char c1, char c2){x = c1; y = c2; return *this;}
      double distance_to(Point){return 7.0;}
      friend  std::ostream& operator<< <>(std::ostream&, const Point<char>&);
    };

    Program: The implementation of the template class Point - point.cc - nothing interesting here.
    // The general implementation of the Point template class.
    
    #include <cmath>
    #include <iostream>
    #include "point.h"
    
    template<typename C> Point<C>::Point(C x_coord, C y_coord):
       x(x_coord), y(y_coord){
    }
    
    template<typename C> Point<C>::Point(): x(C()), y(C()){    // int() is 0 ...
    }
    
    template<typename C> C Point<C>::getx () const{
      return x;
    }
    
    template<typename C> C Point<C>::gety () const{
      return y;
    }
    
    template<typename C> Point<C>& Point<C>::move(C dx, C dy){
        x += dx; y += dy; 
        return *this;
    }
    
    template<typename C> double Point<C>::distance_to(Point<C> p){
        return sqrt((x - p.x) * (x - p.x) + (y - p.y) * (y - p.y));
    }
    
    
    template<typename C> std::ostream& operator<<
                              (std::ostream& s, const Point<C>& p){
      return s << "(" << p.x << "," << p.y << ")" ;
    }

    Program: A program that illustrate the instantiation of both the general and specialized template classes.
    // A program that illustrates the use of both the general Point template class
    // and the specialized Point<char> template class.
    
    #include <iostream>
    #include <string>
    #include "point.cc"           
                                  
    int main(){
      Point<double> pd1,
                    pd2(1,2);
    
      Point<int>    pi1,
                    pi2(3,4);
    
      Point<char>   pc1,           // Using the template specialization.
                                   // The strange default constructor reveals the
                                   // use of the template specialization.
                    pc2(97, 98);   // Char 97 = 'a'
    
      pd2.move(1,1);
      pi2.move(1,1);
      pc2.move('r', 's');          // Absolute moving!
    
      std::cout << "pd2: " << pd2 << std::endl;      // (2,3)
      std::cout << "pi2: " << pi2 << std::endl;      // (4,5)
      std::cout << "pc1: " << pc1 << std::endl;      // (a,z)  !!
      std::cout << "pc2: " << pc2 << std::endl;      // (r,s)
    
      std::cout << "|pd1 - pd2| = " << pd1.distance_to(pd2) << std::endl;    // 3.60555
      std::cout << "|pi1 - pi2| = " << pi1.distance_to(pi2) << std::endl;    // 6.40312
      std::cout << "|pc1 - pc2| = " << pc1.distance_to(pc2) << std::endl;    // 7
    
      std::cout << "|pd1 - pi2| = " << pd1.distance_to(pi2) << std::endl;    // Error: No matching function for call 
                                                                             // to 'Point<double>::distance_to(Point<int>&)' 
    }

    Program: Program output.
    pd2: (2,3)
    pi2: (4,5)
    pc1: (a,z)
    pc2: (r,s)
    |pd1 - pd2| = 3.60555
    |pi1 - pi2| = 6.40312
    |pc1 - pc2| = 7

    Specialization of class templates - complete and partial
    Slide Contents Index
    References 

    We illustrate complete and partial specialization of a class template A

    Program: Full and partial specializations of a class template A.
    #include <iostream>
    #include <string>
    
    template <typename S, typename T> class A{               // The primary template class
      //  ...
    };
    
    template<> class A <int, std::string> {                  // Complete specialization to S = int, T = string
      //  
    };
    
    template<typename S, typename T> class A <S*, T*> {      // Partial Specialization to pointers
      //
    };
    
    template<typename T> class A <T, T> {                    // Partial specialization: T and S are the same types
      //
    };
    
    
    
    int main(){
      A<double,bool> a1;                                     // Use of A<S,T>
      A<int,std::string> a2;                                 // Use of A<int,string>
      A<double*,std::string*> a3;                            // Use of A<T*,S*>
      A<double,double> a4;                                   // Use of A<T,T>
    }

    Program: Extended variant - revealing the specialization via a data member.
    #include <iostream>
    #include <string>
    
    template <typename S, typename T> class A{               // The primary template class
      public: 
        int a;
        S s;
        T t;
        A() : a{1}, s{}, t{} {};
    };
    
    template<> class A <int, std::string> {                  // Complete specialization to S = int, T = string
      public: 
        int a;
        int s;
        std::string t;
        A() : a{2}, s{}, t{}{};
    };
    
    template<typename S, typename T> class A <S*, T*> {      // Partial Specialization to pointers
      public: 
        int a;
        S s;                                                 // If S* is double, then S is the type double
        T t;
        A() : a{3}, s{}, t{}{};
    };
    
    template<typename T> class A <T, T> {                    // Partial specialization: T and S are the same types
      public: 
        int a;
        T s;
        T t;
    
        A() : a{4}, s{}, t{}{};
    };
    
    int main(){
      A<double,bool> a1;                                     // Use of A<S,T>
      A<int,std::string> a2;                                 // Use of A<int,string>
      A<double*,std::string*> a3;                            // Use of A<T*,S*>
      A<double,double> a4;                                   // Use of A<T,T>
    
      std::cout << a1.a << std::endl;   // 1
      std::cout << a2.a << std::endl;   // 2
      std::cout << a3.a << std::endl;   // 3
      std::cout << a4.a << std::endl;   // 4
    
    }


    Collected references
    Contents Index
    About constructors
    The C++ Prog. Lang. (3. edition): Page 306
    The C++ Prog. Lang. (4. edition): Page 582
    The C++ Prog. Lang. (3. edition): Page 307
    The C++ Prog. Lang. (4. edition): Page 513
    The C++ Prog. Lang. (3. edition): Page 310
    The C++ Prog. Lang. (4. edition): Page 585
    C#: Static and dynamic type
    Effective Modern C++: Declare overriding functions override. Item 12
    Effective C++, Third edition: Item 7
    The C++ Prog. Lang. (4. edition): Page 488
    Effective C++, Third edition: Item 33
    Using declarations
    The C++ Prog. Lang. (3. edition): Page 313
    The C++ Prog. Lang. (4. edition): Page 597-599
    The C++ Prog. Lang. (3. edition): Page 851-852
    The C++ Prog. Lang. (4. edition): Page 469
    Effective C++, Third edition: page 43. Item 7
    The C++ Prog. Lang. (4. edition): Page 591-593
    The C++ Prog. Lang. (3. edition): Page 418, 853
    The C++ Prog. Lang. (4. edition): Page 607
    The C++ Prog. Lang. (3. edition): Page 394
    The C++ Prog. Lang. (3. edition): Page 396
    The C++ Prog. Lang. (4. edition): Page 632ff
    Replicated base class
    The C++ Prog. Lang. (3. edition): Page 402, 849
    Friends
    Base class access control
    The C++ Prog. Lang. (3. edition): Page 405
    The C++ Prog. Lang. (4. edition): Page 605
    The C++ Prog. Lang. (3. edition): Page 406
    The C++ Prog. Lang. (3. edition): Page 438
    The C++ Prog. Lang. (4. edition): Page 668ff.
    Generic methods in C#
    Effective Modern C++: template type deduction. Item 1
    The C++ Prog. Lang. (3. edition): Page 338
    The C++ Prog. Lang. (4. edition): Page 930
    The C++ Prog. Lang. (3. edition): Page 341
    The C++ Prog. Lang. (4. edition): Page 721
    vector<bool> as a specialization of vector<T>

     

    Chapter 5: 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: August 1, 2017, 13:30:41