友元类与继承

12
如果我有两个如下的类并且它们之间存在继承关系:
class A
{
    ...
}

class B : public A
{
    ...
}

还有第三个类被定义为A类的友元:

class C
{
    friend class A;
}

如果我将class B定义为class A的一种形式(也是一个对象),那么我是否能够像在最初就将class B定义为友元类一样,访问class C的所有成员?


2
当你尝试的时候会发生什么呢?尝试它所需要的时间要比我们回答你的问题要短。 - Robert Harvey
抱歉,我应该补充说明我已经尝试过了,但它并没有起作用,这让我感到惊讶... - Harry
无法工作,这意味着您无法从B类访问C类的所有成员? - Robert Harvey
1
如果你愿意,可以称之为吹毛求疵,但是B不是A类的一个对象,它是从A类继承而来的。 - Masked Man
@RobertHarvey 是的,如果我试图从类B的函数中访问类C的受保护/私有成员,我会收到“不可见”错误。我想这回答了我的问题,但我还在寻找原因... - Harry
8个回答

24

友谊关系既不是继承的也不是传递式的。它是两个类之间严格的一对一关系。

class A {
  friend class B;  
  int Aries;
};

class B {
  friend class C;  
  int Taurus;
};

class C {
  int Leo;
  void Capricorn() {
    A a;
    a.Aries = 0;  // this wont work, C is not a friend of A.
                // friendship is not transitive
  }
};

class D : public C {
  void Gemini() {
    B b;
    b.Taurus = 0;  // this wont work, D is not a friend of B.
                   // friendship is not inherited
  }
};    

class E : public B {
  void Scorpio() {
    C c;
    c.Leo = 0; // this wont work either, friendship is not inherited
  }
};

参考资料: "C++编程语言" Bjarne Stroustrup 更多说明(我的): 如果友元关系不是一对一的,那么封装就会被破坏。需要注意的是,只有在A类的类声明中将B声明为友元,B类才能访问A的私有成员。B不能强制让A成为自己的友元。
现在,如果友元关系可以被继承,那么只需继承B就可以访问A的私有成员,而A无法阻止它。另外,如果允许友元关系具有传递性,也会引起其他问题,因为现在B可以有一个朋友C,C可以有一个朋友D,一直到Z。所有的B、C、D、...、Z都可以访问A的私有成员,这将是灾难性的。

谢谢。从这个答案中学到了很多。 - Harry
1
@Harry 添加了更多的解释。 :) - Masked Man

6
由于某种原因,每个人都忘记了可以访问从友元类派生的虚拟私有函数。
#include <iostream>

class Friend;
class Parent
{
    friend class Friend;
private:
    virtual void nameYourself() { std::cout << "Parent" << std::endl; }
};

class Child : public Parent
{
private:
    virtual void nameYourself() { std::cout << "Child" << std::endl; }
};

class Friend
{
public:
    void foo(Parent *p) { p->nameYourself(); }
};

int main()
{
    Parent p;
    Child c;
    Friend f;
    f.foo(&p);
    f.foo(&c);
    return 0;
}

运行上述代码的输出结果是:
Parent
Child

它能够工作的原因比较棘手,与传递此指针的方式有关(查找vtable)。如果您从函数声明中删除“virtual”关键字,则会失去这种能力。


1
我相信 OP 问的是如何从 Child 访问 Friend 的私有成员,而不是如何从 Friend 访问 Child 的私有成员。 - azalea

3
引用C++11标准中的话来说,11.3/10部分提到:

友元关系不具有继承性和传递性。

这意味着既不继承友元类的派生类,也不包括友元的朋友能够获得友元关系的好处。


2
以下是我发现有用的遮罩代码的补充示例:
class B {
  friend class F;
  friend class E;
  int Taurus;
};

class E : public B {
  int Egg;
};

class F {
    void Foo () {
       B b;
       b.Taurus = 4;  //Works F is friend of B (of course)
       E e;
       e.Taurus = 6; // Works E is derived from B and F is friend of B 
                  // Taurus is private member of B.
       e.Egg = 5; // Does not work, F is friend of Base Class B but not
               // E, so can't access members of the derived class
    }
};

1
  • 不是继承关系(见下文);如果B是A的子类,C是A的友元,则B无法访问C的私有成员,包括继承的成员。
  • 同样地,如果A是C的友元,或者A和C互为友元,这也不会给B访问C的私有成员,包括继承的成员。
  • 此URL说明友元类的子类不会继承友元关系:

    C++ friend inheritance?

  • 这适用于“关联(主类自己和与主类交友的其他类)的两种情况” - 本问题是针对后一种情况的。


0

我认为这取决于情况。您将可以访问B的A部分(切片部分)。如果您定义了B自己的函数,我认为您将无法访问。


0

我不明白你想做什么,但是B是A的一个超类,不是一个对象。在B和C之间没有任何继承关系。


不,这里A是B的超类。我意识到B和C之间没有继承关系,只有友元关系。 - Harry

0

不可以直接调用 C 类的方法,但是可以通过指针访问它们。需要修改 A 类:

class C
{
  void method()
  {
  }
  friend class A;
};

class A
{
protected:
  constexpr static void (C::*f)() = &C::method;
};

class B : public A
{
  B()
  {
    //C().method(); ← This wont work
    (C().*f)(); // ← This works fine
  }
};

访问数据成员和静态数据也很简单


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接