Friendliness and derived class

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 think I could change it too a->Base::foo()

, but since it foo()

is purely virtual in the class Base

, the call will trigger the call Derived::foo()

anyway.

However, the compiler refuses a->foo()

. I think it makes sense, but I can't figure out why. Am I missing something? Can't (shouldn't) handle this special case?

Thanks.

+2


a source to share


5 answers


When you qualify a method name with a class name, as in Base::foo()

dynamic dispatch (runtime binding) does not apply. It will always call the implementation Base

foo()

, whether it is foo()

virtual or not. Since it is purely virtual in this case, the implementation fails and the compiler complains.

The second problem is that friendship is not inherited in C ++. If you want to Other

have special access to Derived

, he must be a special friend Derived

.

This, on the other hand, works:



Base* a = new Derived();

a->foo(); 

      

Because here you are calling foo()

in Base*

where foo()

is public, and since you are not qualifying foo()

with the class name, it uses dynamic dispatch and ends up calling the Derived

version Foo

.

+6


a source


I think you could do this



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

  a->foo(); 
};

      

+1


a source


Try to put this "friend class"; in a derived class.

Update: Now think about it, I agree with Tyler that you should change a to a Base pointer.

Base* a = new Derived();

      

0


a source


However, the compiler seems to give up on this.

Give up what? It sounds like you are saying that the compiler refuses to allow another to call the function foo () using the Base pointer. It certainly doesn't have to be.

To answer your main question, friendship is not inherited .... period. The scope is checked in the same step as name resolution, and since foo () is protected in the names you use, you cannot call them.

Polymorphism, on the other hand, is resolved by pointer redirection and has nothing to do with name resolution or access permission.

0


a source


This is unfortunate, but friendliness is inherently broken in C ++ in my opinion:

  • Not inherited
  • Grant unlimited access to all internals, there is no way to restrict it.

I have given up using "as-is" and now I use mostly template Key

(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 (maybe even in a straight header), and those who want to give them special access will only do so for that key.

It's not ideal, because you can of course abuse it (key transitivity). But then in C ++ you can abuse everything, so this is more of a Murphy protection problem than a Machiavellian one.

0


a source







All Articles