Let's say I have the following class hierarchy:
class Base
{
protected:
virtual void foo() = 0;
friend class Other;
};
class Derived : public Base
{
protected:
void foo() { /* Some implementation */ };
};
class Other
{
public:
void bar()
{
Derived* a = new Derived();
a->foo(); // Compiler error: foo() is protected within this context
};
};
I guess I could change it too a->Base::foo()
but since foo()
is pure virtual in the Base
class, the call will result in calling Derived::foo()
anyway.
However, the compiler seems to refuse a->foo()
. I guess it is logical, but I can't really understand why. Am I missing something ? Can't (shouldn't) it handle this special case ?
Thank you.
When you qualify a method name with a class name, as in Base::foo()
dynamic dispatch (run-time binding) does not apply. It will always call the Base
implementation of foo()
, no matter if foo()
is virtual or not. Since in this case it is pure virtual, there is no implementation and the compiler complains.
Your second problem is that in C++, friendship is not inherited. If you want Other
to have special access to Derived
, it needs to be a friend of Derived
specifically.
This, on the other hand, works:
Base* a = new Derived();
a->foo();
Because here, you are calling foo()
on a Base*
where foo()
is public, and since you are not qualifying foo()
with a class name, it uses dynamic dispatch and ends up calling the Derived
version of Foo
.
I guess You could do this
void bar()
{
Base* a = new Derived();
a->foo();
};
However, the compiler seems to refuse that.
Refuse what? It sounds like you are saying that the compiler is refusing to allow Other to call the foo() function through a Base pointer. That certainly shouldn't be the case.
To answer your basic question, friendship is not inherited....period. Permission scope is checked at the same stage as name resolution and since foo() is protected within the names you are using, you can't call it.
Polymorphism on the other hand is resolved through pointer redirection and has nothing to do with name resolution or access permission.
Try put this "friend class Other;" in the derived class.
Update: Now think of it, I agree with Tyler that you should change a to a Base pointer.
Base* a = new Derived();
It's unfortunate, but friendliness is inherently broken in C++ in my opinion:
- Not inherited
- Give unrestricted access to all the internals, no possibility to restrict it
I've given up using it "as-is" and I now mostly use the Key
pattern (for lack of a better name).
///
/// Key definition
///
class Friend;
class FriendKey: boost::noncopyable { friend class Friend; FriendKey() {} };
///
/// Base/Derived definition
///
class Base
{
public:
void mySpecialMethod(const FriendKey&) { this->mySpecialMethodImpl(); }
private:
virtual void mySpecialMethodImpl() = 0;
}; // class Base
class Derived: public Base
{
public:
private:
virtual void mySpecialMethodImpl() {}
}; // class Derived
///
/// Friend definition
///
class Friend
{
public:
void mySpecialCall()
{
Derived d;
d.mySpecialMethod(FriendKey());
}
}; // class Friend
The concept is simple: each class declares a key (possible even in the forward header), and those that wish to grant special access to them will only make it possible for this key.
It's not perfect, because you can of course abuse it (by transitivity of the key). But then in C++ you can abuse everything, so it's more a problem of protected against Murphy than Machiavelli.