在C++中这是如何工作的?通过基类暴露派生类的私有虚拟成员

3vpjnl9f  于 11个月前  发布在  其他
关注(0)|答案(3)|浏览(67)

我发现了一个类似的代码片段。让我们假设有一个很好的理由在派生类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()在派生类中是私有的。

tpgth1q7

tpgth1q71#

这里没有什么被忽视。

const A &a = B();
a.print();

字符串
简单地说:你在A的引用上调用printprintA中的public。现在轮到动态分派了。方法是virtual,对象的动态类型是B,所以我们调用B::print
考虑一下,如果这将像您期望的那样工作,则访问将取决于对象的动态类型,并且在这样的代码中,

void foo(const A& a) {
        a.print();
 }


要决定a.print()是否可以被调用是不可能的。但这不是它的工作原理。要决定a.print()是否可以被访问,我们只需要查看A

3lxsmp7m

3lxsmp7m2#

访问控制检查是通过名称进行的,在表达式的静态类型的上下文中使用它。A::print是公共的,所以a.print()是有效的。
虚拟分派与访问控制无关,因此当a绑定到B时,它调用B::print。如果虚拟分派考虑访问控制,则必须将其推迟到运行时进行检查,至少在某些情况下是这样。例如,

extern bool coinflip();
const A &a = coinflip() ? A{} : B{};
a.print();

字符串

ljsrvy3e

ljsrvy3e3#

这有什么出乎意料的?

const A &a = B();
a.print();

字符串
当你调用赋值操作符=时,你可以假设右边的操作符先发生,所以你创建了一个未命名的临时对象class B重要提示:该对象从现在到作用域结束都存在于内存中,因为您正在创建对该对象的引用).在赋值操作符的左边,你说const reference a现在引用该对象。你的reference aclass A的类型,C++允许这种转换(强制转换):Derived classtoBase class。但是,这种转换并没有改变存储class B的未命名对象的内存。因此,当您使用a引用访问print()成员函数时,实际上是在调用B.print()
使用class A类型的引用所做的唯一一件事是,由于print()方法在class A中是公共的,因此您可以在类外部调用它,正如有人在注解中所说的那样。

相关问题