衍生到基础的转换和友谊混淆

时间:2015-10-12 18:25:29

标签: c++ inheritance friend

来自C ++ Primer第5版(D继承自B)

  

如果D使用public或protected从B继承,则从D派生的类的成员函数和朋友可以使用derived-tobase转换。这样   如果D从B私下继承,则代码可能不会使用转换。

是否有任何理由或我是否打算以面值表现?似乎很明显为什么会这样,但它在一个例子中绊倒我:

#include <iostream>
using namespace std;

class Base { 
public: 
    int x = 2;
};

class Derived : protected Base { };

class DerivedAgain : public Derived {
    friend void test();
};

void test() {
    ??? a;
    Base* p = &a;
    cout << p->x;
}

int main(){
    test();
}

我想了解test()在导出到基础转化中对成员x的可访问性。考虑函数???a的{​​{1}}类型的三种可能情况。

  1. test()???Basex的公开成员。在这种情况下没有问题。
  2. Base???。在这种情况下,Derived-to-Base转换是有意义的,因为DerivedAgain具有test() friend的所有成员的DerivedAgain访问权限,包括间接从Base继承的成员。因此,使用指针访问x没有问题。
  3. ???Derived。它汇编得很好。但为什么?我此时感到困惑。 test()Derived类的成员没有特殊访问权限,那么为什么p->x有效,因此Derived-to-Base转换有效?这有效吗?
  4. 确实,如果我将test()的操作更改为

    void test() {
        Derived a;
        cout << a.x;
    }
    

    它没有编译,正如我期望的那样 - 因为x对象继承的Derived成员被protected成为a因此无法被用户使用

    如果我将Base的类型替换为DerivedAgaintest(),则修改后的 @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); VideoView videoView = (VideoView) findViewById(R.id.videoView); videoPosition = videoView.getCurrentPosition(); if (videoView.canPause()) { videoView.pause(); } outState.putInt(VIDEO_POSITION_KEY, videoPosition); Log.d(LOG_TAG, "onSaveInstanceState() Current Position Saved is: " + videoPosition); } 编译正常,正如我所期望的那样。

    我很困惑为什么允许第二级派生类的友元函数使用第一级直接到基础转换,如果该友元函数没有特殊访问权限级别派生类成员。

1 个答案:

答案 0 :(得分:7)

基本上,受保护的继承很奇怪。它编译的原因是,从N4527的[class.access.base]开始:

  

如果是 R ,<{1}}的基类B 可访问    - N的发明公共成员将成为B或其中的公共成员    - R 出现在班级N的成员或朋友中,N的发明公共成员将是私人或   受保护的B成员,或者    - R 发生在源自N的类P的成员或朋友中,N的发明公共成员会   是B
的私人或受保护成员    - 存在一个类PS R 可访问的B基类,而SS的基类1}}可访问   在 R

这里的第三个要点是相关要点。 R 发生在从Ntest)派生的类PDerivedAgain)的朋友(N)中,并且DerivedB)的发明公共成员将成为BaseP)的受保护成员。

我之前认为接受此代码是gcc错误( bug 67493 ),但现在我认为不接受它是一个铿锵的错误 - 尽管{{3}另外指出,存在相关的标准缺陷(T.C.)。那里的措辞变化仅适用于成员访问,而与我们相关的是基本访问。但也许gcc只是实施标准规则(接受是正确的)而clang遵循这个缺陷报告的逻辑结论(目前有效CWG #1873)并且不允许它。

同样,受保护的继承真的很奇怪。欢迎来到C ++的精彩世界。