Exercise index of this lecture   Alphabetic index   Course home   

Exercises and solutions
Abstraction Mechanisms, Part 2


5.1   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.

Solution

In this variant of the program, the aB object from main is sliced already when it is passed to f. Therefore, x in f refers to an instance of class A. Both via y, z, and w, op() returns the value of a from clas A. Namely 3.

Here is my solution, with some comments:

// Class B inherits from A. Illustration of slicing.
// A minor variant of the previous program.

#include <iostream>
#include <string>

using namespace std;

class A {
public:
  int a;

  A(): a(3){
  }

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

class B: public A {
public:
  int b;

  B():  b(5){
  }

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

int f(A x){        // Was   int f(B &x)   in the original version. Now the parameter is passed by value. Sliced during parameter passing.
  A  y = x,        
    *z = &x,       
    &w = x;        

  cout << y.op() << endl;    // 3.  A operation. y is an A object (sliced when passed to f).
  cout << z->op() << endl;   // 3.  z is a pointer to an instance of A.
  cout << w.op() << endl;    // 3.  z is a reference to an instance of A.
                             // So did anything change?   YES - A LOT 
}

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


5.2   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.

Solution

Take a look at this program:

// 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)
}

The function vf() in B, which overrides vf() from B, happens to hide the inherited vf(double) from A.

You may override vf(double) in B as well, but this is not really what we want. We want that the inherited A::vf(double) to be visible in B. The following trick will do it:

#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:
  using A::vf;                              // vf from A is now available in B.

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

                                            // It is expected that A::vf(double) is inherited.
                                            // And indeed, it is in this version.
}; 


int main(){
  B *b = new B();
  
  b->vf();
  b->vf(5.0);                               // Compiles in this version.
}


You should pay attention to the using declaration using A::vf added to the public part of B. This makes all vf names from A usable in B.


5.3   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).


5.4   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.

Solution

Please read section 21.3.5.1 page 634 in The C++ Prog. Lang. (4. edition) for clarification. See also the comments of the two programs referred in the execercise text.

Remember that a constructor in a virtual base must be called exactly once. Which of class B or C should control the activation of the constructor in A?

The answer is that this must be decided by the most derived base, in our case D. Therefore class D directly controls (in the initialization list of D) which A constructor to activate for the D-object.


5.5   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?

Solution

Here is a program that explains my findings, see the comments:

// A class D with both a private and public base class, and where frD is a friend of class D.
// Solution to exercise. Does not compile. The errors are marked and explained in the comments.

#include <iostream>
#include <string>

using namespace std;

class D;

class B {
//  friend void frD(D &ad);
private:
  int b;
public:
  B(int b): b(b){}
  void Bop(){                                   // Bop is public in B
    cout << "Bop()" << endl;
  }

};

class C {
//  friend void frD(D &ad);
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 {
  friend void frD(D &ad);
private:
  int d;
public:
  D(int b, int c, int d): B(b), C(c), d(d){} 
  void Dop(){                                   // Dop is public in D
    cout << "Dop()" << endl;
    Bop();                                      
    Cop();                                      
  }

};

// The friend of D:
void frD(D &ad){
  ad.Dop();                 // OK. Dop is public
  cout << ad.d << endl;     // OK. frD is a friend of D
  ad.Bop();                 // OK. B is a private base, Bop becomes private in D, frD is a friend of D.
  ad.Cop();                 // OK. C is public in D.

  cout << ad.b << endl;     // Illegal: b is private in B. 
  cout << ad.c << endl;     // Illegal: c is private in C. Even with public inheritance!
}

// A non-friend of D: Here we nickname it an 'enemy', therefore the name enD:
void enD(D &ad){
  ad.Dop();                 // OK. Dop is public
  cout << ad.d << endl;     // Illegal: enD is not a friend.
  ad.Bop();                 // Illegal: Because B is a private base, Bop is private in D.
  ad.Cop();                 // OK: Because C is a public base, Cop is public i D.
  cout << ad.b << endl;     // Illegal: b is private in B. 
  cout << ad.c << endl;     // Illegal: c is private in C. Even with public inheritance!
}

int main(){
  D d(1,2,3);
  frD(d);
  enD(d);
}

If also frD becomes of friend of B and C, the access to b and c is legal from frD. Remember, a friend of D is not a friend of the bases of D.

Also remember that friendship is not inherited. Therefore, it is not sufficient for frD to be a friend of B and C alone.


5.6   No single most general class in C++  

Discuss the question asked on this slide.

Solution

This exercise is supposed to give rise to some discussion - there is no solution as such. However:


5.7   A template class with friends  

Arrange 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.

Solution

Here is the header file point.h:

template<class C> class Point;
template<class C> std::ostream& operator<<(std::ostream& s, const Point<C>& p);

template<class C>class Point {
private: 
  C x, y;

public:
  Point(C, C);
  Point();
  C getx () const;
  C gety () const;
  Point<C>& move(C, C);
  C distance_to(Point);
  friend  std::ostream& operator<< <>(std::ostream&, const Point<C>&);
};

Notice the two declarations before the generic class point defintion. And notice the <> before the parameters in the friend declaration

Here is the point.cc:

#include <cmath>
#include <iostream>
#include "point.h"

template<class C> Point<C>::Point(C x_coord, C y_coord):
   x(x_coord), y(y_coord){
}

template<class C> Point<C>::Point(): x(0.0), y(0.0){
}

template<class C> C Point<C>::getx () const{
  return x;
}

template<class C> C Point<C>::gety () const{
  return y;
}

template<class C> Point<C>& Point<C>::move(C dx, C dy){
    x += dx; y += dy; 
    return *this;
}

template<class C> C Point<C>::distance_to(Point p){
    return sqrt((x - p.x) * (x - p.x) + (y - p.y) * (y - p.y));
}


template<class C> std::ostream& operator<<
                          (std::ostream& s, const Point<C>& p){
  return s << "(" << p.x << "," << p.y << ")" ;
}



Finally, prog.cc with main:

#include <iostream>
#include <string>
#include "point.cc"           // Notice inclusion of cc files, in order to 
                              // instantiate the templates 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, 3ed ed, page 350-351.
int main(){
  Point<double> pd1,
                pd2(1,2);

  Point<int>    pi1,
                pi2(3,4);

  Point<char>   pc1,
                pc2(97, 98);   // Char 97 = 'a'

  pd2.move(1,1);
  pi2.move(1,1),
  pc2.move(1,1),

  std::cout << "pd2: " << pd2 << std::endl;      // (2,3)
  std::cout << "pi2: " << pi2 << std::endl;      // (4,5)
  std::cout << "pc2: " << pc2 << std::endl;      // (b,c)
}


Generated: Tuesday August 1, 2017, 13:31:10