C++_继承与多态双双环绕,虚函数若被private限定,多态执行的函数又是谁(2/2)
题目:下面函数输出结果是( )
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* pa = (A*)new B;
pa->f();
return 0;
}
A .B::f()
B .A::f(),因为子类的f()函数是私有的
C .A::f(),因为强制类型转化后,生成一个基类的临时对象,pa实际指向的是一个基类的临时对象
D .编译错误,私有的成员函数不能在类外调用
题目分析
B类继承A类,即使不强转也可以直接赋值,因为赋值兼容规则作出了保证。
赋值兼容规则补充
将派生类对象 赋值给基类对象/指针/引用
Base* p = new Derive;// 派生类对象指针 -> 基类指针
Derive d;
Base& r = d;// 派生类对象 赋值给 基类引用
Base b = d;// 派生类对象 赋值给 基类对象
第一句代码:
A* pa = (A*)new B;
C的说法很离谱啊:为甚么这么说呢?
new B确实会初始化一个B的对象,new后返回B的对象的地址,这个地址原本指向的是B的对象。但是该地址赋值给pa。pa向上转型成为基类地址,那么pa只会指向B的对象中的基类对象的部分。
故排除C。
第二句代码:
pa->f();
继上篇文章,我们知道首先判断是不是多态:基类指针调用(多态条件一满足),调用虚函数(多态条件二满足)。于是f()的具体表现,应该是B类中的f()。
但问题的关键是:
那问题的关键是:B 将重写的虚函数声明为 private,是否会影响通过基类指针的调用,进而影响多态的结果;又或者根本编译不过去?
访问权限与多态的关系
在 C++ 中,虚函数的访问权限是由调用者的静态类型(即指针或引用的类型)决定的,而不是由动态类型(即实际对象的类型)决定的。
好理解:访问权限在编译阶段检查(起效),编译器看到f()通过A*调用,立即就去A类作用域检查A::f()的访问权限:
- 如果是private,那么对外就不应该访问,编译报错。
- 如果是public,那么允许类外调用,编译通过。
而我们说的多态,则是起效在运行阶段。
-
运行时,由于f()是虚函数,且pa实际指向的是B对象,因此会调用B::f()。(从底层来说,编好的指令里,call的函数地址,是从B的对象的虚表里取到的函数地址)
所以,这个场景就好比:
一天小明的父母要出差,李华一个人在家,就给李华饭钱,并问李华打算晚餐吃什么——(这个“问”,就是编译阶段)
李华回答:“买一点菜自己下厨。”(下厨,是基类函数定义)
父母表示很满意,放心地走了(编译通过)
正到了饭点,这个饭钱的使用者是李华,李华直接点外卖(点外卖,虚函数执行,产生多态效果)——bot原创😎读者看个乐呵就行
所以回到这道题:只要在编译阶段,检查到调用的函数对应的静态类型作用域(A:: )里函数的限定符为public。它的编译就能通过,不会去影响运行阶段调用实际指向对象的虚函数。
因此本题选A。
所以,即使派生类里写private,影响不了多态的行为。而会影响什么?
int main()
{
B b;
b.f(); // 这句编译就报错了,这就是派生类里对虚函数加私有访问的结果。派生类对象自己访问不了
return 0;
}
致谢D老师的摘要:
摘要:题目考察C++中虚函数的访问权限与多态行为。当基类指针指向派生类对象时,虚函数调用权限由指针的静态类型(基类)决定,而实际执行则由动态类型(派生类)决定。虽然派生类B将虚函数f()设为private,但由于基类A中的f()是public,通过A*调用f()能编译通过,运行时仍会调用B::f(),输出B::f()。因此正确答案是A。访问权限检查在编译阶段进行,不影响运行时的多态行为。
更多推荐


所有评论(0)