Department of Engineering

IT Services

C++ Inheritance, Protection and Friends

This document assumes you're familiar with the idea of inheritance and controlling access to items in a class. The earlier examples revise some inheritance ideas.

Look at this program, paying particular attention to the public and private usage.

class base {
public:
  int number1;
};

class kid: public base {
public:
  int number2;
private:
  int number3;
};

int main() 
{
  kid d;
  base b;
  d.number1=555;
  d.number2=777;
  d.number3=999; 
}

A line of this code doesn't compile. Let's see why.

pubpriv.figpublic is being used in 2 ways. We'll deal first with its role in inheritance. The class kid inherits the components of the base. The way it's done here (using public base) means that the privacy of the base components is preserved in kid - i.e. number1 is public in base so it's public in kid too, and thus it can be accessed from outside kid. So d.number1=555; is legal.

Inside kid, number2 is public and number3 is private. This means that number2 can be accessed from elsewhere but number3 can only be accessed from inside the kid class - from a member function. Consequently d.number2=777; is legal but d.number3=999; is a compile error.

In the next example the protected keyword is used. protected is like private except that derived classes can also access the component. The kid class has been given a constructor function. Have a look at this code and try to work out which lines are illegal.

class base {
public:
  int number1;
protected:
  int number2;
private:
  int number3;
};

class kid: public base {
public:
  int number;
  kid() {
   number1=1;
   number2=2;
   number3=3;
  };
};

int main() 
{
  kid d;
  base b;
  d.number1=555;
  d.number2=777;
  d.number3=999; 
}

The line number3=3; in the constructor is illegal - in the base class number3 is private so even derived classes can't touch it. The lines d.number2=777;, and d.number3=999; are illegal too because only public components can be accessed from outside their class. If you're not convinced, see what your compiler says.

Friends

Sometimes (we'll see why later) you might want a function to have all the access privileges of a member function without being inside a class. That's where the friend keyword is useful.

In the code below, the routine function is trying to change private and protected values inside a variable of type kid. Normally it wouldn't be allowed to do this, but the kid class now has a line making the routine function a friend, so the code compiles. If you comment out the friend line and try to compile, you should get an error message.

class base {
public:
  int number1;
protected:
  int number2;
private:
  int number3;
};

class kid: public base {
  friend void  routine(kid& d);
private:
  int number4;
};

void routine(kid& d) {
  d.number2=999; 
  d.number4=111;
}

int main() 
{
  kid d;
  base b;
  routine(d);
}

Note that

  • The privileges of friendship aren't inherited. If routine was base's friend, that doesn't automatically mean that routine is kid's friend too.
  • Friends can be either functions or other classes. If a class is a friend, all the class's routines are friends.
  • Friendship is granted, not taken - a function can't become a friend of a class unless the class agrees.

Why Make Friends?

The rule of thumb is to make member functions when you can, and friends when you must. Sometimes you have no choice, though efficiency issues might be relevant.

The following code creates a new type called base. The code's ok except for the final cout line, which is likely to make compilers produce many error messages.

#include <iostream>
using namespace std;

class base {
public:
  base(int n) { number1=n; number2=n;};
  int number1;
private:
  int number2;
};

int main()
{
  base b(99);
  cout << b << endl;
}

The trouble is that cout doesn't know how to print this new entity. Suppose we want it to print out the number1 component. We can add a routine to tell << what to do in this situation. If you add this function to the code above it will compile.

ostream &operator <<(ostream &stream, base b)
  {
    cout << b.number1;
    return stream;
};

Overloading an operator in this way makes output easier in the main code, though the overloading code itself isn't trivial. Fortunately you don't need to understand it to use and adapt it. This routine needs to return the stream so that you can chain several output expressions together, but that's not why this can't be a member function of base. The operator takes 2 operands and these need to be in the order shown because that's how the operator's used. The trouble is that the member function would only be called if the base operand were on the left of the operator.

Suppose we also wanted to print number2? The new function doesn't have access to this private component, but we've already said that the function can't be a member function of base, so what can we do? The answer is to add this line to the base class definition so that the routine becomes a friend.

 friend ostream &operator<<(ostream &, base);

References