C++ 默认参数与继承产生的有趣现象
默认参数在编译阶段确定,函数的版本在运行时确定
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>
using namespace std;
class A
{
public:
virtual void print(int a = 10)
{
cout << "A : " << a << endl;
}
};
class B:public A
{
public:
void print(int a = 20)
{
cout << "B : " << a << endl;
}
};
int main()
{
A a;
B b;
A *pA = &b;
a.print();
b.print();
pA->print();
return 0;
}
答案
大家可以先试着思考一下为什么,我当时是真心没有想出个所以然,然后搜索关键字估计不对也没找到,后来问了老工程师,讲解了好久才知道了是编译时候发生的事,但是也半天才能接受过来这个解释
1
2
3
A : 10
B : 20
B : 10
我的最终理解
想必很多人都这样想,打印的结果一定是”A : 10”或者”B : 20”。觉得这道题的考点应该在,子类地址赋值给基类后,运行的是谁的版本。然而我错了
- 以下是我最后的理解,不担保一定正确
一句话总结就是:默认参数是编译时确定的,函数地址(运行哪个版本)是运行时确定的!
这里牵涉到两个关键词:动态联编、虚函数表
我也是学习中,就不贴概念理论了,大家可以百度一下
子类重写基类的虚函数时,其实对虚函数表中,对应的函数地址做了覆盖,也就是说,用子类自己的函数地址覆盖了原本的位置。如此,当运行以下语句时,调用的则是子类中版本。
1
pA->print();
然后为什么默认参数却用了基类的版本呢?是因为这个默认参数是在编译时确定的,也就是说,被认为类型为 A 的对象(不管是原本就是 A,还是指向子类 B 的父类指针 A*)他的默认参数都将使用 a=10。这个是在编译阶段,编译器检测到该语句时就确认的。
个人意淫这个过程:啊咧?这里有个 A 要调用 print,还不带参数,朕恩赐你一个默认参数吧,你是 A,就给你 10。而编译器并不知道运行时这个 pA 其实来自于 B 类型的 b 对象。
This post is licensed under CC BY 4.0 by the author.