前些日子,有个同学问我一个关于虚函数的缺省參数问题。他是从某个论坛上看到的。可是自己没想通。便来找我。

如今分享一下这个问题。先看一小段代码:

#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++ 虚函数的缺省參数问题的更多相关文章

  1. 标C编程笔记day06 动态分配内存、函数指针、可变长度參数

    动态分配内存:头文件 stdlib.h     malloc:分配内存     calloc:分配内存,并清零     realloc:调整已分配的内存块大小     演示样例:         in ...

  2. C语言中函数和指针的參数传递

    近期写二叉树的数据结构实验.想用一个没有返回值的函数来创建一个树,发现这个树就是建立不起来,那么我就用这个样例讨论一下c语言中指针作为形參的函数中传递中隐藏的东西. 大家知道C++中有引用的概念,两个 ...

  3. JMeter 压力測试使用函数和 CSV 文件參数化 json 数据

            在 http Load Testing 中.json 数据的提交是个让人头疼的问题.本文具体介绍怎样进行 JMeter 的 json 測试提交,以及怎样将其參数化.        St ...

  4. Effective JavaScript Item 21 使用apply方法调用函数以传入可变參数列表

    本系列作为Effective JavaScript的读书笔记. 以下是一个拥有可变參数列表的方法的典型样例: average(1, 2, 3); // 2 average(1); // 1 avera ...

  5. C语言中main函数的參数具体解释

    main函数的定义形式         main函数能够不带參数,也能够带參数,这个參数能够觉得是 main函数的形式參数.C语言规定main函数的參数仅仅能有两个,习惯上这两个參数写为argc和ar ...

  6. Python学习笔记7:函数对象及函数对象作參数

    一.lambda函数 比如: fun1 = lambda x,y: x + y print fun1(3,4) 输出:7 lambda生成一个函数对象.该函数參数为x,y,返回值为x+y.函数对象赋给 ...

  7. Effective JavaScript Item 55 接受配置对象作为函数參数

    接受配置对象作为函数參数 尽管保持函数接受的參数的顺序非常重要,可是当函数可以接受的參数达到一定数量时.也会让用户非常头疼: var alert = new Alert(100, 75, 300, 2 ...

  8. Objective-C学习笔记(二十一)——函数的返回值与參数类型

    我们在之前的博客中涉及到的函数都没有參数,同一时候返回值也为void,即不须要返回值. 可是在以后的开发中.函数返回值和參数是必须涉及到的. 所以如今我们来讨论这个问题.我们还是以People类为例. ...

  9. C中參数个数可变的函数

    一.什么是可变參数 我们在C语言编程中有时会遇到一些參数个数可变的函数,比如printf()函数,其函数原型为: int printf( const char* format, ...); 它除了有一 ...

随机推荐

  1. 2015.06.11,技术,关于Matlab中的Jbtest检验

    总体分布的正态性检验一般采取Jarque-Bera检验方法. 1. JBTest检验的定义: 在统计学中,Jarque-Bera检验是对样本数据是否具有符合正态分布的偏度和峰度的拟合优度的检验.该检验 ...

  2. 敏捷开发 —— TDD(测试驱动开发)

    测试驱动开发 TDD(Test-Driven Development)是敏捷开发的一项核心实践,同时也是一种设计技术和方法. 既然是测试驱动,便是测试,测试用例先行: 首先编写好测试用例,期待值,实际 ...

  3. 视图 Model转集合

    @{    Layout = null;}@using MvcApplication2.Models <!DOCTYPE html> <html><head>    ...

  4. 五分钟DBA:浅谈伪分布式数据库架构

    [IT168 技术]12月25日消息,2010互联网行业技术研讨峰会今日在上海华东理工大学召开.本次峰会以“互联网行业应用最佳实践”为主题,定位于互联网架构设计.应用开发.应用运维管理,同时,峰会邀请 ...

  5. JAVA在线观看视频教程完整版

    今天给大家介绍一下JAVA在线观看视频教程完整版,我们知道Java是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由Sun Microsystems公司于1995年5月推出的Java程序设计语 ...

  6. ajax请求及遍历

    $(".btn").click(function(){ $.ajax({ type:"POST", url:"../../js/info.json&q ...

  7. Pyhton学习——Day46

    # 数据库:存储数据的仓库# 数据库更多的是安全.备份# 客户端取服务端的数据实际都是从服务端的内存中抓取数据# 数据库管理系统软件# 数据库管理系统(Database Management Syst ...

  8. css控制单行或者多行文本超出显示省略号

    1.单行文本 使用text-overflow:ellipsis属性 text-overflow: clip|ellipsis|string; clip:修剪文本. ellipsis:显示省略符号来代表 ...

  9. APICloud 上滑加载更多

    <!DOCTYPE html><html>        <head>        <meta charset="UTF-8">  ...

  10. d3代码如何改造成update结构(恰当处理enter和exit)

    d3的enter和exit 网上有很多blog讲解.说的还凑合的见:https://blog.csdn.net/nicolecc/article/details/50786661 如何把自己的rude ...