views:

91

answers:

5

Hi,

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.

A: 

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();
grokus
Thanks. I'd like to avoid it (if I can).
ereOn
+4  A: 

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.

Tyler McHenry
Actually when I specify `a->Base::foo()` the compiler seems ok with it. Are you sure about this ?
ereOn
Did you go all the way through to the linking step? The linker should complain about an undefined reference to the `Base::foo` method.
Tyler McHenry
@Tyler McHenry: I wasn't to the linking step, you're right. Thanks for the alternative/more correct method.
ereOn
A: 

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.

Noah Roberts
My bad... I edited my question and removed the ambiguity regarding "that" which was intended to refer to `a->foo()`.
ereOn
+1  A: 

I guess You could do this

void bar()
{
  Base* a = new Derived();

  a->foo(); 
};
Maciej Hehl
A: 

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.

Matthieu M.