views:

96

answers:

3

I have a class with a protected method Zig::punt() and I only want it to be accessible to the class "Avocado". In C++, you'll normally do this using the "friend Avocado" specifier, but this will cause all of the other variables to become accessible to "Avocado" class; I don't want this because this breaks encapsulation.

Is what I want impossible, or does there already exist an obscure trick out there that I can use to achieve what I want? Or possibly alternative class design patterns that'll achieve the same thing?

Thanks in advance for any ideas!

A: 

You can add a proxy to the Zig class

class Foo
{
    private:
        int m_x, m_y;
    public:
        class Bar
        {
            friend class Baz;
            int& x(Foo& blubb)
            {
                return blubb.m_x;
            }
        };
        friend class Bar;
};

class Baz
{
    public:
        void grml(Foo& f)
        {
            Foo::Bar b;
            // Yes, this looks awful
            b.x(f) = 42;
        }
};

void z()
{
    Foo f;
    Baz b;
    b.grml(f);
}
Rudi
Your point might be better made if you used the same names present in the original question. `Foo` and `Baz` are no less nonsensical than `Zig` and `Avocado`.
Dennis Zickefoose
+4  A: 

Here's an ugly yet working trick:

class AvocadoFriender {
protected:
  virtual void punt() = 0;
  friend class Avocado; 
}

class Zig : public AvocadoFriender {
  ...
protected:
  void punt();
}

Basically you add a mixin class that exposes only the part of the interface that you want to Avocado. We take advantage of the fact that by inheriting a class that is befriended to Avocado you don't expose anything more except what was exposed originally.

Kornel Kisielewicz
+2  A: 

I personally like the Key pattern.

class WannaBeFriend { /**/ };

class WannaBeFriendKey: boost::noncopyable
{
  friend class WannaBeFriend;
  WannaBeFriendKey () {}
};

And now:

class LimitedAccess
{
public:
  Item& accessItem(const WannaBeFriendKey&) { return mItem; }

private:
  Item mItem;
  Item mOtherItem;
};

I really like this solution because:

  • you only need a forward declaration (like for friendship)
  • you don't have the full access granted by friendship, instead only a limited access is granted, under the full control of the class writer
  • moreover it's perfectly clear what can be accessed and from whom, thus easing debugging
  • this access can be granted to child classes of WannaBeFriend: it only needs exposing a protected: static const WannaBeFriend& Key(); (may or may not apply)

And of course, it's very likely that the compiler will optimize the passing of this reference since it does not serve any purpose, so it does not corrupt the design nor add unnecessary temporaries :)

Matthieu M.