Friday, September 24, 2010

Friends and Inhertance

In C++ there is a common gotcha with friends and inheritance.

The rules are summarized as
  1. Friend access is one way.
  2. Friends of a friend have no access.
  3. Children of a friend have no access.
  4. Children of a parent with a friend will allow the friend to access them like the parent.

    IE friend access of a derived class is limited to interface present on the original base class only. Virtual functions will operate as normal if they are accessible.

Example code;

#include <iostream>

using namespace std;

class FriendOfFriendA;
class FriendA;


class A{    
public:
    A() : stuff(8) {}

    int get(){return stuff;}
    
protected:
    virtual int vget(){return stuff;}
    int stuff;

    friend class FriendA;
};

class KidA : public A {
public:
    KidA() : A(), kidStuff(9) { stuff = 16; }

protected:
    int kidGet(){return kidStuff;}
    virtual int vget(){return kidStuff;}
    int kidStuff;
};

class FriendA{
public:
    int getA(A& a){return a.stuff;}
    int getKidA(KidA& a){return a.stuff;}
    //int getKidAstuff(KidA a){return a.kidStuff;}  //BAD only a's stuff is a friend
    //int getKidAstuff(KidA a){return a.kidGet();}  //BAD only a's stuff is a friend

    int getVirtualA(A* a){return a->vget();}

    friend class FriendOfFriendA;

    static int pubStaticGetA(A& a) { return a.stuff; }
private:
    static int privStaticGetA(A& a) { return a.stuff; }
};

class FriendOfFriendA{
public:
    //int getADDirect(A& a){return a.stuff;}   //BAD no freind of a friend access
    int getAIndirect(A& a){return FriendA::pubStaticGetA(a);}
    int getADoubleIndirect(A& a){return FriendA::privStaticGetA(a);}
};

class FriendAKid : public FriendA{
public:
    //int getADDirect(A& a){return a.stuff;} //BAD no child of a friend access
    int getADIndirect(A& a){return getA(a);}
};


int main()
{
    A    a;
    KidA kidA;

    FriendA         friendA;
    FriendAKid      friendAKid;
    FriendOfFriendA friendOfFriendA;

    cout << "friendA.getA(a) : " << friendA.getA(a) << endl;
    cout << "friendA::pubStaticGetA(a) : " << FriendA::pubStaticGetA(a) << endl;

    cout << "friendA.getVirtualA(a) : " << friendA.getVirtualA(&a) << endl;
    cout << "friendA.getVirtualA(kidA) : " << friendA.getVirtualA(&kidA) << endl;
   
    cout << "friendA.getKidA(kidA) : " << friendA.getKidA(kidA) << endl;
    
    //cout << "friendOfFriendA.getADDirect(a) : " << friendOfFriendA.getADDirect(a) << endl;
    cout << "friendOfFriendA.getAIndirect(a) : " << friendOfFriendA.getAIndirect(a) << endl;
    cout << "friendOfFriendA.getADoubleIndirect(a) : " << friendOfFriendA.getADoubleIndirect(a) << endl;
    
    cout << "friendAKid.getADIndirect(a) : " << friendAKid.getADIndirect(a) << endl;
}

This produces this result:
friendA.getA(a) : 8
friendA::pubStaticGetA(a) : 8
friendA.getVirtualA(a) : 8
friendA.getVirtualA(kidA) : 9
friendA.getKidA(kidA) : 16
friendOfFriendA.getAIndirect(a) : 8
friendOfFriendA.getADoubleIndirect(a) : 8
friendAKid.getADIndirect(a) : 8

No comments:

Post a Comment