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!
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.
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 :)
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);
}