来自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}}类型的三种可能情况。
test()
是???
。 Base
是x
的公开成员。在这种情况下没有问题。Base
是???
。在这种情况下,Derived-to-Base转换是有意义的,因为DerivedAgain
具有test()
friend
的所有成员的DerivedAgain
访问权限,包括间接从Base
继承的成员。因此,使用指针访问x
没有问题。???
是Derived
。它汇编得很好。但为什么?我此时感到困惑。 test()
对Derived
类的成员没有特殊访问权限,那么为什么p->x
有效,因此Derived-to-Base转换有效?这有效吗?确实,如果我将test()
的操作更改为
void test() {
Derived a;
cout << a.x;
}
它没有编译,正如我期望的那样 - 因为x
对象继承的Derived
成员被protected
成为a
因此无法被用户使用
如果我将Base
的类型替换为DerivedAgain
和test()
,则修改后的 @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);
}
编译正常,正如我所期望的那样。
我很困惑为什么允许第二级派生类的友元函数使用第一级直接到基础转换,如果该友元函数没有特殊访问权限级别派生类成员。
答案 0 :(得分:7)
基本上,受保护的继承很奇怪。它编译的原因是,从N4527的[class.access.base]开始:
如果是 R ,<{1}}的基类
B
可访问 -N
的发明公共成员将成为B
或其中的公共成员 - R 出现在班级N
的成员或朋友中,N
的发明公共成员将是私人或 受保护的B
成员,或者 - R 发生在源自N
的类P
的成员或朋友中,N
的发明公共成员会 是B
或
的私人或受保护成员 - 存在一个类P
,S
是 R 可访问的B
基类,而S
是S
的基类1}}可访问 在 R 。
这里的第三个要点是相关要点。 R 发生在从N
(test
)派生的类P
(DerivedAgain
)的朋友(N
)中,并且Derived
(B
)的发明公共成员将成为Base
(P
)的受保护成员。
我之前认为接受此代码是gcc错误( bug 67493 ),但现在我认为不接受它是一个铿锵的错误 - 尽管{{3}另外指出,存在相关的标准缺陷(T.C.)。那里的措辞变化仅适用于成员访问,而与我们相关的是基本访问。但也许gcc只是实施标准规则(接受是正确的)而clang遵循这个缺陷报告的逻辑结论(目前有效CWG #1873)并且不允许它。
同样,受保护的继承真的很奇怪。欢迎来到C ++的精彩世界。