为什么允许调用派生类?通过基类指针的私有虚拟方法?
# 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()。我知道它是如何工作的,但为什么允许使用此代码?
# 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;
}
This code works correctly and prints B::f(). I know how it works, but why is this code allowed?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
访问控制是在编译时而不是运行时执行的。通常,对
f()
的调用无法了解ptr
所指向的对象的运行时类型,因此不会检查派生类的访问说明符。这就是允许调用的原因。至于为什么 B 类被允许使用私有函数重写 - 我不确定。当然,B 违反了从 A 继承所隐含的接口,但一般来说,C++ 语言并不总是强制接口继承,因此它完全错误的事实并不意味着 C++ 会阻止您。
所以我猜想这个类 B 可能有一些用例 - 替换仍然适用于动态多态性,但静态 B 不能替代 A(例如,可以有调用
f
的模板,即可以使用 A 作为参数,但不能使用 B 作为参数)。在某些情况下,这可能正是您想要的。当然,这可能只是其他一些考虑的意外结果。Access control is performed at compile time, not runtime. There's no way in general for the call to
f()
to know the runtime type of the object pointed to byptr
, so there's no check on the derived class's access specifiers. That's why the call is permitted.As for why class B is permitted to override using a private function at all - I'm not sure. Certainly B violates the interface implied by its inheritance from A, but in general the C++ language doesn't always enforce inheritance of interface, so the fact that it's Just Plain Wrong doesn't mean C++ will stop you.
So I'd guess that there's probably some use case for this class B - substitution still works with dynamic polymorphism, but statically B is not a substitute for A (e.g. there can be templates that call
f
, that would work with A as argument but not with B as argument). There may be situations where that's exactly what you want. Of course it could just be an unintended consequence of some other consideration.该代码是允许的,因为 f 在 A 的接口中是公共的。派生类不能更改基类的接口。 (重写虚拟方法不会更改接口,隐藏基类的成员也不会更改,尽管两者似乎都可以这样做。)如果派生类可以更改基类的接口,则它将违反 “是”关系。
如果 A 的设计者想让 f 不可访问,那么它应该被标记为 protected 或 private。
This code is allowed because f is public in A's interface. A derived class cannot change the interface of a base class. (Overriding a virtual method isn't changing the interface, nor is hiding members of a base, though both can appear to do so.) If a derived class could change a base's interface, it would violate the "is a" relationship.
If the designers of A want to make f inaccessible, then it should be marked protected or private.
除了史蒂夫的回答之外:
In addition to Steve's answer:
您的基类正在为所有继承的子类定义接口。我不明白为什么它应该阻止上述访问。您可以尝试从“B”派生一个类并使用 Base 接口来调用 ,这会导致错误。
干杯!
Your base class is defining the interface for all the inherited children. I do not see why it should prevent the mentioned access. You can try deriving a class down from 'B' and use the Base interface to call , which would result in an error.
Cheers!
函数访问控制检查发生在 C++ 函数调用的后期。
高层的顺序类似于名称查找、模板参数推导(如果有)、重载解析,然后访问控制(公共/保护/私有)检查。
但是在您的代码片段中,您使用了指向基类的指针,并且基类中的函数 f() 确实是公共的,这是编译器在编译时可以看到的范围,因此编译器肯定会让您的代码片段通过。
但以上所有这些都是在编译时发生的,因此它们实际上是静态的。虽然虚拟函数调用通常由 vtable 和 vtable 提供支持。 vpointer 是在运行时发生的动态东西,因此虚拟函数调用与访问控制正交(虚拟函数调用发生在访问控制之后),这就是为什么对 f() 的调用实际上结束了 B::f() 无论访问控制是私有的。
但是,如果您尝试使用,
尽管 vpointer & 仍然无法通过。 vtable,编译器在编译时不会允许它编译。
但如果你尝试:
这会很好用。
Function access control check happens in later stage of a c++ function call.
The order in high level would be like name lookup, template argument deduction(if any), overload resolution, then access control(public/protect/private) check.
But in your snippet, you were using a pointer to base class and function f() in base class is indeed public, that's as far as compiler can see at compiler time, so compiler will certain let your snippet pass.
But all those above are happens at compile time so they are really static. While virtual function call often powered by vtable & vpointer are dynamic stuff which happens at runtime, so virtual function call is orthogonal to access control(virtual function call happens after access control),that's why the call to f() actually ended B::f() regardless is access control is private.
But if you try to use
This will not pass despite the vpointer & vtable, compiler will not allow it to compile at compile time.
But if you try:
This would work just fine.
与 Java 非常相似,在 C++ 中,您可以增加方法的可见性,但不能降低它。
Pretty much like in Java, in C++ you can increase the visibility of methods but not decrease it.