我发现了一个类似的代码片段。让我们假设有一个很好的理由在派生类B中隐藏虚函数。但是这个私有成员可以通过基类暴露,如下所示:
class A {
public:
virtual void print() const { std::cout << "hello A" << std::endl;}
};
class B: public A {
private:
void print() const override { std::cout << "hello B" << std::endl;}
};
int main() {
const A &a = B();
a.print(); // works unexpectedly (prints "hello B")
const B &b = B();
b.print(); // does not work (as expected)
}
字符串
为什么a.print()
可以工作,即使A在运行时应该动态绑定到B类?或者C++在运行时忽略私有/公共分类器?
我执行了上面的代码,并不期望a.print()
工作,因为print()
在派生类中是私有的。
3条答案
按热度按时间tpgth1q71#
这里没有什么被忽视。
字符串
简单地说:你在
A
的引用上调用print
。print
是A
中的public
。现在轮到动态分派了。方法是virtual
,对象的动态类型是B
,所以我们调用B::print
。考虑一下,如果这将像您期望的那样工作,则访问将取决于对象的动态类型,并且在这样的代码中,
型
要决定
a.print()
是否可以被调用是不可能的。但这不是它的工作原理。要决定a.print()
是否可以被访问,我们只需要查看A
。3lxsmp7m2#
访问控制检查是通过名称进行的,在表达式的静态类型的上下文中使用它。
A::print
是公共的,所以a.print()
是有效的。虚拟分派与访问控制无关,因此当
a
绑定到B
时,它调用B::print
。如果虚拟分派考虑访问控制,则必须将其推迟到运行时进行检查,至少在某些情况下是这样。例如,字符串
ljsrvy3e3#
这有什么出乎意料的?
字符串
当你调用赋值操作符
=
时,你可以假设右边的操作符先发生,所以你创建了一个未命名的临时对象class B
(重要提示:该对象从现在到作用域结束都存在于内存中,因为您正在创建对该对象的引用).在赋值操作符的左边,你说const referencea
现在引用该对象。你的referencea
是class A
的类型,C++允许这种转换(强制转换):Derived classtoBase class。但是,这种转换并没有改变存储class B
的未命名对象的内存。因此,当您使用a
引用访问print()
成员函数时,实际上是在调用B.print()
。使用
class A
类型的引用所做的唯一一件事是,由于print()
方法在class A
中是公共的,因此您可以在类外部调用它,正如有人在注解中所说的那样。