C++ 虚函数的缺省參数问题
前些日子,有个同学问我一个关于虚函数的缺省參数问题。他是从某个论坛上看到的。可是自己没想通。便来找我。
如今分享一下这个问题。先看一小段代码:
#include <iostream> using namespace std; class A
{
public:
virtual void Fun(int number = 10)
{
cout << "A::Fun with number " << number;
}
}; class B: public A
{
public:
virtual void Fun(int number = 20)
{
cout << "B::Fun with number " << number << endl;
}
}; int main()
{
B b;
A &a = b;
a.Fun(); return 0;
}
问题是,这段代码输出什么?正确答案是:B::Fun with number 10
这个问题并不难,关键要看你对C++了解多少。
我了解得不多。可是这个小问题恰好能答上来。非常明显。这段代码的输出结果依赖于C++的多态。什么是多态?在C++中。多态表现为指向父类对象的指针(或引用)指向子类对象,然后利用父类指针(或引用)调用它实际指向的子类的成员函数。
这些成员函数由virtualkeyword定义。也就是所谓的虚函数。
假设你知道C++的多态是怎么回事,那么这道题目你至少能答对前半部分。
也就是说,输出的前半部分应该是这种:B::Fun with number。疑点在于,到底number等于10还是20?
这就涉及到C++的静态绑定和动态绑定问题。说到静态绑定和动态绑定,就不能不谈“静态类型”和“动态类型”。何为静态类型呢?C++标准(2003)是这么说的:
1.3.11 static type [defns.static.type]
the type of an expression (3.9), which type results from analysis of the program without considering execu-
tion semantics. The static type of an expression depends only on the form of the program in which the
expression appears, and does not change while the program is executing.
什么又是动态类型呢?
1.3.3 dynamic type [defns.dynamic.type]
the type of the most derived object (1.8) to which the lvalue denoted by an lvalue expression refers. [Exam-
ple: if a pointer (8.3.1) p whose static type is “pointer to class B” is pointing to an object of class D, derived
from B (clause 10), the dynamic type of the expression *p is “D.” References (8.3.2) are treated similarly. ]
The dynamic type of an rvalue expression is its static type.
假设你感觉标准写得点深奥,不easy懂。那就来看看C++ Primer(第4版。15.2.4)怎么说的吧:“基类类型引用和指针的关键点在于静态类型(static type,在编译时可知的引用类型或指针类型)和动态类型(dynamic type。指针或引用所绑定的对象的类型。这是仅在执行时可知的)可能不同”。
在该书第15章的最后,对静态类型和动态类型做了一个总结:静态类型是指“编译时类型。对象的静态类型与动态类型同样。引用或指针所引用的对象的动态类型能够不同于引用或指针的静态类型”;动态类型是指“执行时类型。基类类型的指针和引用能够绑定到派生类型的对象。在这样的情况下。静态类型是基类引用(或指针),但动态类型是派生类引用(或指针)”。
这下就明确多了。静态类型是编译期就能确定的类型,简单地说,当你声明一个变量时。为该变量指定的类型就是它的静态类型。动态类型是在程序执行时才干确定的类型,典型样例就是父类对象指针指向子类对象,这时,父类指针的动态类型就变成了子类指针。
正如上述C++标准中所举的样例。如果p原本是一个B类型的指针。如果如今让p指向D对象。而D恰好是B的派生类,那么p的动态类型就是D类型的指针。听上去有点绕,为了方便说明。我还是拿出C++标准上的一个样例来分析:
struct A {
virtual void f(int a = 7);
};
struct B : public A {
void f(int a);
};
void m()
{
B* pb = new B;
A* pa = pb;
pa->f(); // OK, calls pa->B::f(7)
pb->f(); // error: wrong number of arguments for B::f()
}
这段代码中,pb的静态类型是B类型指针,它的动态类型也是B类型指针。
pa的静态类型是A类型指针,而它的的动态类型却是B类型指针。
一旦明确了静态类型和动态类型的概念,静态绑定和动态绑定也就好理解了。依照C++ Primer的说法。动态绑定是指“延迟到执行时才选择执行哪个函数。
在C++中。动态绑定指的是在执行时基于引用或指针绑定的对象的基础类型而选择执行哪个virtual函数”。
显然。动态绑定与虚函数是息息相关的。与此相应,静态绑定就简单多了:假设一个类型的成员函数不是虚函数。那也就没什么好选择的了。通过指针或引用调用成员函数时,直接绑定到指针或引用的基础类型就可以。比方。在上面的代码中,pa->f()。这里调用的实际上是B的成员函数f(),也就是说,被调用的是与pa的动态类型相相应的函数。这就是所谓的“动态绑定”。
说了这么多,来解释本文一開始给出的问题。
在C++中,尽管虚函数的调用是通过动态绑定来确定的,可是虚函数的缺省參数却是通过静态绑定确定的。(就这么规定的,据说是为了提高效率)显然,a的静态类型是A的引用。而动态类型是B的引用。因此。当a调用虚函数Fun()时。依据动态绑定规则,它调用的是B的成员函数Fun();而对于虚函数的缺省參数,依据静态绑定规则。它将number确定为A中给出的缺省值10。
再简单说一下本文给出的第二段代码。这是C++标准中给出的一个样例。并且也给了说明:“A virtual function call (10.3) uses the default arguments in the declaration of the virtual function determined by the static type of the pointer or reference denoting the object. An
overriding function in a derived class does not acquire default arguments from the function it overrides.” 我来翻译一下吧:“调用虚函数时使用的缺省參数在虚函数声明中给出,这些缺省參数由指示对象的指针或引用的静态类型确定。
派生类中的重写函数无法获得它所重写的函数的缺省參数。”
C++ 虚函数的缺省參数问题的更多相关文章
- 标C编程笔记day06 动态分配内存、函数指针、可变长度參数
动态分配内存:头文件 stdlib.h malloc:分配内存 calloc:分配内存,并清零 realloc:调整已分配的内存块大小 演示样例: in ...
- C语言中函数和指针的參数传递
近期写二叉树的数据结构实验.想用一个没有返回值的函数来创建一个树,发现这个树就是建立不起来,那么我就用这个样例讨论一下c语言中指针作为形參的函数中传递中隐藏的东西. 大家知道C++中有引用的概念,两个 ...
- JMeter 压力測试使用函数和 CSV 文件參数化 json 数据
在 http Load Testing 中.json 数据的提交是个让人头疼的问题.本文具体介绍怎样进行 JMeter 的 json 測试提交,以及怎样将其參数化. St ...
- Effective JavaScript Item 21 使用apply方法调用函数以传入可变參数列表
本系列作为Effective JavaScript的读书笔记. 以下是一个拥有可变參数列表的方法的典型样例: average(1, 2, 3); // 2 average(1); // 1 avera ...
- C语言中main函数的參数具体解释
main函数的定义形式 main函数能够不带參数,也能够带參数,这个參数能够觉得是 main函数的形式參数.C语言规定main函数的參数仅仅能有两个,习惯上这两个參数写为argc和ar ...
- Python学习笔记7:函数对象及函数对象作參数
一.lambda函数 比如: fun1 = lambda x,y: x + y print fun1(3,4) 输出:7 lambda生成一个函数对象.该函数參数为x,y,返回值为x+y.函数对象赋给 ...
- Effective JavaScript Item 55 接受配置对象作为函数參数
接受配置对象作为函数參数 尽管保持函数接受的參数的顺序非常重要,可是当函数可以接受的參数达到一定数量时.也会让用户非常头疼: var alert = new Alert(100, 75, 300, 2 ...
- Objective-C学习笔记(二十一)——函数的返回值与參数类型
我们在之前的博客中涉及到的函数都没有參数,同一时候返回值也为void,即不须要返回值. 可是在以后的开发中.函数返回值和參数是必须涉及到的. 所以如今我们来讨论这个问题.我们还是以People类为例. ...
- C中參数个数可变的函数
一.什么是可变參数 我们在C语言编程中有时会遇到一些參数个数可变的函数,比如printf()函数,其函数原型为: int printf( const char* format, ...); 它除了有一 ...
随机推荐
- JS 正则表达式的位置匹配ZZ
http://regexpal.com/ 上面这个网站可以用于在线检测JS的正则表达式语法 除了熟知的几个固定字符表示位置: ^ : Match the beginning of the string ...
- android:QQ多种側滑菜单的实现
在这篇文章中写了 自己定义HorizontalScrollView实现qq側滑菜单 然而这个菜单效果仅仅是普通的側拉效果 我们还能够实现抽屉式側滑菜单 就像这样 第一种效果 另外一种效果 第三种效果 ...
- nginx报 File not found 错误
原因可能非常多,但对于刚開始学习的人.大部分应该是/etc/nginx/conf.d/default.conf里面的php解析部分配置不正确. 解决的话就是把root定义.在server下加上,这样r ...
- JNI DETECTED ERROR IN APPLICATION: input is not valid Modified UTF-8: illegal start byte 0xfe
JNI DETECTED ERROR IN APPLICATION: input is not valid Modified UTF-8: illegal start byte 0xfe 在使用Jni ...
- 英语发音规则---X字母
英语发音规则---X字母 一.总结 一句话总结: 1.x位于词尾或音节尾部,读/ks/? box /bɒks/ n.盒; 箱状物 fix /fɪks/ vt.固定 fox /fɒks/ n.狐; 狐狸 ...
- kaggle 中使用ipython
# pandas import pandas as pd from pandas import Series,DataFrame # numpy, matplotlib, seaborn import ...
- CaffeExample 在CIFAR-10数据集上训练与测试
本文主要来自Caffe作者Yangqing Jia网站给出的examples. @article{jia2014caffe, Author = {Jia, Yangqing and Shelhamer ...
- mybatis的二级缓存的使用
1.引入ehcache的jar包和mybatis整合ehcache的jar包: <!-- ehchache --> <dependency> <groupId>ne ...
- Android GreenDao 使用教程
上一篇 总结了grendao 环境搭建以及简单的增删查改,接下来将全面解析框架的使用,基于上篇的orm模型(Note)数据库讲解 GreenDao的插入: 插入的方式有很多: daoSession.g ...
- C++下面关于字符串数组的一些操作
今天在写一个搜索引擎的分词系统,是很简单的那种,但是居然费了我一天的时间还没完成,晚上估计还得弄一会了,但是在这个过程中,遇到了集中关于字符串数组的操作,值得和大家分享一下. 首先是关于统计字符串数组 ...