Why access qualifier is not considered when virtual function is overridden?
以下代码打印了"我是B!".有点奇怪,因为
由于访问限定符不是类方法签名的一部分,因此可能会导致这种奇怪的情况。当虚函数被重写时,为什么不考虑C++访问限定符?我能禁止这样的案件吗?这个决定背后的设计原则是什么?
活生生的例子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | #include <iostream> class A { public: virtual void foo() { std::cout <<"I'm A! "; }; }; class B: public A { private: void foo() override { std::cout <<"I'm B! "; }; }; int main() { A* ptr; B b; ptr = &b; ptr->foo(); } |
你有多个问题,所以我会逐一回答。
当虚函数被重写时,为什么不考虑C++访问限定符?因为在所有重载解析之后,编译器都会考虑访问限定符。这种行为由标准规定。
例如,请参见关于CPPreference:
Member access does not affect visibility: names of private and privately-inherited members are visible and considered by overload resolution, implicit conversions to inaccessible base classes are still considered, etc. Member access check is the last step after any given language construct is interpreted. The intent of this rule is that replacing any private with public never alters the behavior of the program.
下一段描述了您的示例所演示的行为:
Access rules for the names of virtual functions are checked at the call point using the type of the expression used to denote the object for which the member function is called. The access of the final overrider is ignored.
另请参阅此答案中列出的操作顺序。
我能禁止这样的案件吗?不。
我认为你永远也做不到,因为在这种行为中没有违法的地方。
这个决定背后的设计原则是什么?只是为了澄清:通过这里的"决策",我暗示编译器在过载解析之后检查访问限定符的规定。简短的回答是:防止在更改代码时出现意外。
要了解更多详细信息,我们假设您正在开发一些类似于此的
1 2 3 4 5 6 | class CoolClass { public: void doCoolStuff(int coolId); // your class interface private: void doCoolStuff(double coolValue); // auxiliary method used by the public one }; |
假设编译器可以基于公共/私有说明符进行重载解析。然后以下代码将成功编译:
1 2 3 | CoolClass cc; cc.doCoolStuff(3.14); // invokes CoolClass::doCoolStuff(int) // yes, this would raise the warning, but it can be ignored or suppressed |
然后在某个时刻,您发现您的私有成员函数实际上对类客户机有用,并将其移动到"公共"区域。这会自动改变先前存在的客户机代码的行为,因为现在它调用
因此,应用访问限定符的规则是以不允许这种情况的方式编写的,因此您将在一开始就得到"含糊不清的调用"编译器错误。而虚拟函数并不是特殊情况,原因是相同的(见此答案)。
是否可以将其视为封装冲突?不是真的。通过将指向类的指针转换为指向其基类的指针,您实际上会说:"在此,我希望将此对象B用作对象A"-这是完全合法的,因为继承意味着"原样"关系。
因此,问题是,您的示例是否可以被视为违反了基类规定的契约?看来是的,可以。
有关其他解释,请参阅此问题的答案。
附笔。别误会我,这并不意味着你不应该使用私有虚拟功能。相反,它通常被认为是一个好的实践,看这条线。但他们应该是非常底层阶级的私有。因此,归根结底,您不应该使用私有虚拟函数来破坏公共契约。
除非您有意通过指向接口/基类的指针强制客户机使用您的类。但有更好的方法可以做到这一点,我相信这些讨论超出了这个问题的范围。
访问限定符(如
当调用
Can it be considered as encapsulation violation?
不,因为接口已经通过继承发布,所以没有。
用派生类中的