Friendness and derived class

好久不见. 提交于 2019-12-12 20:17:22

问题


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.


回答1:


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.




回答2:


I guess You could do this

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

  a->foo(); 
};



回答3:


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.




回答4:


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();



回答5:


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.



来源:https://stackoverflow.com/questions/2767525/friendness-and-derived-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!