# include <iostream>
using namespace std;
class A
{
public:
virtual void f()
{
cout << "A::f()" << endl;
}
};
class B:public A
{
private:
virtual void f()
{
cout << "B::f()" << endl;
}
};
int main()
{
A *ptr = new B;
ptr->f();
return 0;
}
此代码正常工作并打印B :: f()。我知道它是如何工作的,但为什么允许这个代码?
答案 0 :(得分:14)
访问控制在编译时执行,而不是在运行时执行。调用f()
通常无法知道ptr
指向的对象的运行时类型,因此不会检查派生类的访问说明符。这就是允许通话的原因。
至于为什么B级被允许使用私有功能覆盖 - 我不确定。当然B违反了它从A继承所暗示的接口,但一般来说C ++语言并不总是强制继承接口,所以它的Just Plain Wrong并不意味着C ++会阻止你。
所以我猜这个类B可能有一些用例 - 替换仍然适用于动态多态,但静态B不能替代A(例如,可以有调用f
的模板,可以使用A作为参数,但不能使用B作为参数)。可能存在这种情况,这正是您想要的。当然,这可能只是其他一些考虑因素的意外结果。
答案 1 :(得分:2)
允许使用此代码,因为f在A的界面中是公共的。派生类不能更改基类的接口。 (重写虚方法不是更改接口,也不是隐藏基类的成员,尽管两者都可以这样做。)如果派生类可以更改基类的接口,则会违反"is a" relationship。< / p>
如果A的设计者想要使其无法访问,则应将其标记为受保护或私有。
答案 2 :(得分:0)
您的基类正在为所有继承的子级定义接口。我不明白为什么它应该阻止上述访问。您可以尝试从'B'派生一个类并使用Base接口进行调用,这将导致错误。
干杯!
答案 3 :(得分:0)
除了史蒂夫的回答:
答案 4 :(得分:0)
函数访问控制检查发生在c ++函数调用的后期。 高级别的顺序将类似于名称查找,模板参数推断(如果有),重载解析,然后是访问控制(公共/保护/私有)检查。
但是在你的代码片段中,你使用了一个指向基类的指针,而基类中的函数f()确实是公共的,这就像编译器在编译时可以看到的那样,因此编译器肯定会让你的代码段通过。
A *ptr = new B;
ptr->f();
但是所有这些都是在编译时发生的,所以它们确实是静态的。虚拟功能调用通常由vtable&amp; vpointer是在运行时发生的动态内容,因此虚函数调用与访问控制正交(虚函数调用在访问控制之后发生),这就是为什么对f()的调用实际上结束了B :: f(),无论访问控制是私有的
但是如果你尝试使用
B* ptr = new B;
ptr->f()
尽管vpointer&amp; vtable,编译器不允许它在编译时编译。
但如果你尝试:
B* ptr = new B;
((static_cast<A*>(ptr))->f();
这样可以正常工作。
答案 5 :(得分:-1)
非常类似于Java,在C ++中,您可以提高方法的可见性,但不会降低它。