精华内容
下载资源
问答
  • 因此,重定义缺省参数值的唯一方法是重定义一个继承而来的函数。然而,重定义继承而来的非虚函数是一种错误(参见条款37),所以,我们完全可以把讨论的范围缩小为 "继承一个有缺省参数值的虚函数" 的

    昨晚在chgaowei的博客上关于讨论C++虚函数的默认参数问题,刚翻书找了一下,在Effective C++ 中的38条有说明。

    直接上原文吧,最后加几句细点的理解

    条款38: 决不要重新定义继承而来的缺省参数值

    让我们从一开始就把问题简化。缺省参数只能作为函数的一部分而存在;另外,只有两种函数可以继承:虚函数和非虚函数。因此,重定义缺省参数值的唯一方法是重定义一个继承而来的函数。然而,重定义继承而来的非虚函数是一种错误(参见条款37),所以,我们完全可以把讨论的范围缩小为 "继承一个有缺省参数值的虚函数" 的情况。

    既然如此,本条款的理由就变得非常明显:虚函数是动态绑定而缺省参数值是静态绑定的。

    什么意思?你可能会说你不懂这些最新的面向对象术语;或者,过度劳累的你一时想不起静态和动态绑定的区别。那么,让我们来复习一下。

    对象的静态类型是指你声明的存在于程序代码文本中的类型。看下面这个类层次结构:

    用图形来表示是下面这样:

                    Shape

                        //

                       /  /

                      /    /

       Rectangle    Circle

    现在看看这些指针:

    Shape *ps;                      // 静态类型 = Shape*

    Shape *pc = new Circle;         // 静态类型 = Shape*

    Shape *pr = new Rectangle;      // 静态类型 = Shape*

    这个例子中, ps pc,和pr都被声明为Shape指针类型,所以它们都以此作为自己的静态类型。注意,这和它们真的所指向的对象的类型绝对没有关系 ---- 它们的静态类型总是Shape*

    对象的动态类型是由它当前所指的对象的类型决定的。即,对象的动态类型表示它将执行何种行为。上面的例子中,pc的动态类型是Circle*pr的动态类型是Rectangle*。至于ps,实际上没有动态类型,因为它(还)没有指向任何对象。

    动态类型,顾名思义,可以在程序运行时改变,典型的方法是通过赋值:

    ps = pc;                        // ps的动态类型

                                    // 现在是Circle*

    ps = pr;                        // ps的动态类型

                                    // 现在是Rectangle*

    虚函数是动态绑定的,意思是说,虚函数通过哪个对象被调用,具体被调用的函数就由那个对象的动态类型决定:

    pc->draw(RED);                  // 调用Circle::draw(RED)

    pr->draw(RED);                  // 调用Rectangle::draw(RED)

    我知道这些都是老掉牙的知识了,你当然也了解虚函数。(如果想知道它们是怎么实现的,参见条款M24)但是,将虚函数和缺省参数值结合起来分析就会产生问题,因为,如上所述,虚函数是动态绑定的,但缺省参数是静态绑定的。这意味着你最终可能调用的是一个定义在派生类,但使用了基类中的缺省参数值的虚函数:

    pr->draw();                     // 调用Rectangle::draw(RED)!

    这种情况下,pr的动态类型是Rectangle*,所以Rectangle的虚函数被调用 ---- 正如我们所期望的那样。Rectangle::draw中,缺省参数值是GREEN。但是,由于pr的静态类型是Shape*,这个函数调用的参数值是从Shape类中取得的,而不是Rectangle类!所以结果将十分奇怪并且出人意料,因为这个调用包含了ShapeRectangle类中Draw的声明的组合。你当然不希望自己的软件以这种方式运行啦;至少,用户不希望这样,相信我。

    不用说,ps pc,和pr都是指针的事实和产生问题的原因无关。如果它们是引用,问题也会继续存在。问题仅仅出在,draw是一个虚函数,并且它的一个缺省参数在子类中被重新定义了。

    为什么C++坚持这种有违常规的做法呢?答案和运行效率有关。如果缺省参数值被动态绑定,编译器就必须想办法为虚函数在运行时确定合适的缺省值,这将比现在采用的在编译阶段确定缺省值的机制更慢更复杂。做出这种选择是想求得速度上的提高和实现上的简便,所以大家现在才能感受得到程序运行的高效;当然,如果忽视了本条款的建议,就会带来混乱。

     

    先说一下现在的C++的参数传递机制,非虚函数和虚函数的传递机制都是一样的。

    比如如下函数调用:

    func(10) ;

    会被编译器翻译成:

    push 10

    puch 返回地址

    call func

    大概就是这样:先把参数压栈,然后压返回地址,在调用函数。

    对于虚函数来说,如果调用时没有指定参数值,那么编译器会帮我们加上去。

    对,加上去,这里就有问题来了,如果是用基类指针调用的虚函数,我们知道,因为动态绑定,编译器暂时无法知道实际调用的是哪个函数,所以他得用虚函数的机制进行2此指针操作在实际的类地址中找到虚函数表,再根据偏移找到实际的函数跳转地址,而此时,编译器必须提前把参数压栈准备好,call之后就直接用参数了。

    那么,既然编译器还不知道实际调用的是哪个函数,那么当然就更不知道实际传递的默认参数应该是子类还是父类的了。关键就在这里。

    参数都是静态绑定的,如果要动态,上文说了,效率会跟虚函数调用一样稍微有点低,所以C++折中了。

    话又说回来,如果要动态的实现,怎么办呢???下面说点个人的思路

    对,虚函数动态绑定是用vptbl实现的(这个《深度探索c++对象模型》中有)那么默认参数的实现是否也能参考呢??应该可以。

    编译器可以把一条压栈(压默认参数值的指令)放在函数代码的前面几条指令中

    然后在跳转的时候,实际的call指令可以延后几句,这样:

    用effective 中的例子:

    pr->draw();  

    编译后可能是:

    //因为没有指定参数,所以在我们的尝试实现中不压参数

    call ( *pr->_vtbl[2] - 4 )  //这个计算虚函数地址的我简单写到一行了,实际上时2个取指操作。-4的原因待会说。

    当然,函数代码也得相应的改变一下:

    push GREEN

    Rectangle::draw://这才是真正的draw代码,上述-4的原因就是为了执行上面的push GREEN指令,这,同理在基类中代码也会变成这样:

    push RED

    Shape::draw:  //Shape 的draw代码

    总结一下,就是说把参数压栈的指令稍微改一下,如果正常传递了参数当然就不用了,如果没传递参数,那么在函数的代码之前加上一句压参数指令,然后在函数跳转的时候,往回多跳一条指令,让实际掉用的代码去压这个参数

    说的可能有的乱,有些错的地方还忘见谅。呵呵···

     

     

    展开全文
  • 昨晚在chgaowei的博客上关于讨论C++虚函数的默认参数问题,刚翻书找了一下,在Effective C++ 中的38条有说明。 直接上原文吧,最后加几句细点的...因此,重定义缺省参数值的唯一方法是重定义一个继承而来的函数。...

    昨晚在chgaowei的博客上关于讨论C++虚函数的默认参数问题,刚翻书找了一下,在Effective C++ 中的38条有说明。

    直接上原文吧,最后加几句细点的理解

    条款38: 决不要重新定义继承而来的缺省参数值

    让我们从一开始就把问题简化。缺省参数只能作为函数的一部分而存在;另外,只有两种函数可以继承:虚函数和非虚函数。因此,重定义缺省参数值的唯一方法是重定义一个继承而来的函数。然而,重定义继承而来的非虚函数是一种错误(参见条款37),所以,我们完全可以把讨论的范围缩小为 "继承一个有缺省参数值的虚函数" 的情况。

    既然如此,本条款的理由就变得非常明显:虚函数是动态绑定而缺省参数值是静态绑定的。

    什么意思?你可能会说你不懂这些最新的面向对象术语;或者,过度劳累的你一时想不起静态和动态绑定的区别。那么,让我们来复习一下。

    对象的静态类型是指你声明的存在于程序代码文本中的类型。看下面这个类层次结构:

    enum ShapeColor { RED, GREEN, BLUE }; // 一个表示几何形状的类 class Shape { public: // 所有的形状都要提供一个函数绘制它们本身 virtual void draw(ShapeColor color = RED) const = 0; ... }; class Rectangle: public Shape { public: // 注意:定义了不同的缺省参数值 ---- 不好! virtual void draw(ShapeColor color = GREEN) const; ... }; class Circle: public Shape { public: virtual void draw(ShapeColor color) const; ... };

    用图形来表示是下面这样:

    Shape

    /\

    / \

    / \

    Rectangle Circle

    现在看看这些指针:

    Shape *ps; // 静态类型 = Shape*

    Shape *pc = new Circle; // 静态类型 = Shape*

    Shape *pr = new Rectangle; // 静态类型 = Shape*

    这个例子中, ps pc,和pr都被声明为Shape指针类型,所以它们都以此作为自己的静态类型。注意,这和它们真的所指向的对象的类型绝对没有关系 ---- 它们的静态类型总是Shape*

    对象的动态类型是由它当前所指的对象的类型决定的。即,对象的动态类型表示它将执行何种行为。上面的例子中,pc的动态类型是Circle*pr的动态类型是Rectangle*。至于ps,实际上没有动态类型,因为它(还)没有指向任何对象。

    动态类型,顾名思义,可以在程序运行时改变,典型的方法是通过赋值:

    ps = pc; // ps的动态类型

    // 现在是Circle*

    ps = pr; // ps的动态类型

    // 现在是Rectangle*

    虚函数是动态绑定的,意思是说,虚函数通过哪个对象被调用,具体被调用的函数就由那个对象的动态类型决定:

    pc->draw(RED); // 调用Circle::draw(RED)

    pr->draw(RED); // 调用Rectangle::draw(RED)

    我知道这些都是老掉牙的知识了,你当然也了解虚函数。(如果想知道它们是怎么实现的,参见条款M24)但是,将虚函数和缺省参数值结合起来分析就会产生问题,因为,如上所述,虚函数是动态绑定的,但缺省参数是静态绑定的。这意味着你最终可能调用的是一个定义在派生类,但使用了基类中的缺省参数值的虚函数:

    pr->draw(); // 调用Rectangle::draw(RED)!

    这种情况下,pr的动态类型是Rectangle*,所以Rectangle的虚函数被调用 ---- 正如我们所期望的那样。Rectangle::draw中,缺省参数值是GREEN。但是,由于pr的静态类型是Shape*,这个函数调用的参数值是从Shape类中取得的,而不是Rectangle类!所以结果将十分奇怪并且出人意料,因为这个调用包含了ShapeRectangle类中Draw的声明的组合。你当然不希望自己的软件以这种方式运行啦;至少,用户不希望这样,相信我。

    不用说,ps pc,和pr都是指针的事实和产生问题的原因无关。如果它们是引用,问题也会继续存在。问题仅仅出在,draw是一个虚函数,并且它的一个缺省参数在子类中被重新定义了。

    为什么C++坚持这种有违常规的做法呢?答案和运行效率有关。如果缺省参数值被动态绑定,编译器就必须想办法为虚函数在运行时确定合适的缺省值,这将比现在采用的在编译阶段确定缺省值的机制更慢更复杂。做出这种选择是想求得速度上的提高和实现上的简便,所以大家现在才能感受得到程序运行的高效;当然,如果忽视了本条款的建议,就会带来混乱。

    先说一下现在的C++的参数传递机制,非虚函数和虚函数的传递机制都是一样的。

    比如如下函数调用:

    func(10) ;

    会被编译器翻译成:

    push 10

    puch 返回地址

    call func

    大概就是这样:先把参数压栈,然后压返回地址,在调用函数。

    对于虚函数来说,如果调用时没有指定参数值,那么编译器会帮我们加上去。

    对,加上去,这里就有问题来了,如果是用基类指针调用的虚函数,我们知道,因为动态绑定,编译器暂时无法知道实际调用的是哪个函数,所以他得用虚函数的机制进行2此指针操作在实际的类地址中找到虚函数表,再根据偏移找到实际的函数跳转地址,而此时,编译器必须提前把参数压栈准备好,call之后就直接用参数了。

    那么,既然编译器还不知道实际调用的是哪个函数,那么当然就更不知道实际传递的默认参数应该是子类还是父类的了。关键就在这里。

    参数都是静态绑定的,如果要动态,上文说了,效率会跟虚函数调用一样稍微有点低,所以C++折中了。

    话又说回来,如果要动态的实现,怎么办呢???下面说点个人的思路

    对,虚函数动态绑定是用vptbl实现的(这个《深度探索c++对象模型》中有)那么默认参数的实现是否也能参考呢??应该可以。

    编译器可以把一条压栈(压默认参数值的指令)放在函数代码的前面几条指令中

    然后在跳转的时候,实际的call指令可以延后几句,这样:

    用effective 中的例子:

    pr->draw();

    编译后可能是:

    //因为没有指定参数,所以在我们的尝试实现中不压参数

    call ( *pr->_vtbl[2] - 4 ) //这个计算虚函数地址的我简单写到一行了,实际上时2个取指操作。-4的原因待会说。

    当然,函数代码也得相应的改变一下:

    push GREEN

    Rectangle::draw://这才是真正的draw代码,上述-4的原因就是为了执行上面的push GREEN指令,这,同理在基类中代码也会变成这样:

    push RED

    Shape::draw: //Shape 的draw代码

    总结一下,就是说把参数压栈的指令稍微改一下,如果正常传递了参数当然就不用了,如果没传递参数,那么在函数的代码之前加上一句压参数指令,然后在函数跳转的时候,往回多跳一条指令,让实际掉用的代码去压这个参数

    说的可能有的乱,有些错的地方还忘见谅。呵呵···

    展开全文
  • 缺省参数的静态绑定的定义:调用一个派生类中virtual函数时,使用的是基类中为该函数指定的缺省参数值 这样是非常危险的情况,所以在Effective C++ 条款37中:绝不重新定义继承而来的缺省参数值 可以看下面的一段...

    缺省参数的静态绑定的定义:调用一个派生类中virtual函数时,使用的是基类中为该函数指定的缺省参数值

    这样是非常危险的情况,所以在Effective C++ 条款37中:绝不重新定义继承而来的缺省参数值


    可以看下面的一段程序:

    #include <iostream>
    using namespace std;
    
    class A
    {
    public:
    	void test()
    	{
    		func1();
    	}
    	virtual void func1(int a = 1)
    	{
    		cout<<"A~~"<<a<<endl;
    	}
    };
    
    class B:public A
    {public:
    	void func1(int a = 2)
    	{
    		cout<<"B~~"<<a<<endl;
    	}
    };
    int main(int argc,char** argv)
    {
    	B *p = new B;
    	p->test();
    	cin.get();
    	return 0;
    }


    执行过程:

    1:由于B类中未覆盖基类A中的虚函数,所以调用的是基类A中的test函数

    2:虚函数执行动态绑定,所以调用的func1函数是派生类B中的

    3:函数的缺省参数执行静态绑定,所以func1函数中的a使用的是基类中的缺省参数1

    4:输出结果~


    结果为:


    展开全文
  • 自学python——Day12(关于缺省参数和多值参数) 一. 缺省参数 ...··关于函数定义形参为元组或字典的拆包 不进行拆包操作时,如图 ,传入的元组和字典的实参都保存在args元组中 ··正确写法: ...

    自学python——Day12(关于缺省参数和多值参数)

    一. 缺省参数

    示例
    在这里插入图片描述

    注:缺省参数只能放在函数定义参数的末尾位置

    二. 多值参数

    在定义函数的参数时,分别定义接收元组和字典的形参
    *args :接收元组
    **kwargs :接收字典

    示例
    在这里插入图片描述

    ··关于函数定义形参为元组或字典的拆包

    不进行拆包操作时,如图 ,传入的元组和字典的实参都保存在args元组中
    在这里插入图片描述

    ··正确写法:
    在这里插入图片描述

    展开全文
  • 我们都知道,在类定义了带参数的构造函数后,便不再提供默认构造函数,但是子类的构造函数需要父类的缺省构造函数才能定义,否则编译时将会出错,这时有两种解决方法,一种是利用构造函数重载,重载一个缺省的构造...
  • 关于C++中的缺省参数

    2006-03-23 11:00:00
    C++函数定义往往使用缺省参数,给变量赋默认值。但,缺省值应该放到后面,也就是说如果参数使用默认值,那么它的后面就不能出现实参了。比如: void Fun( int i=0,int j = 0; int k = 0);函数调用:Fun();//正确Fun...
  • 其实不知道哪种说法正确,但我所经历的情况是二种都可以,以后有时间了再去仔细研究一下,先记下来 六、函数参数的默认值 (本讲重点概念)C++允许实参个数与形参个数不同。办法是在形参表列中对一个或几个形参指定...
  • C++ Primer中关于缺省构造函数的叙述

    千次阅读 2011-10-19 16:21:26
    只有当没有构造函数或声明了缺省构造函数时,...尤其是,如果一个类声明了一个包含多个参数的构造函数,但没有声明缺省构造函数,则每个类对象的定义都必须提供所需的实参。 class MyClass { public: MyClass(i
  • 关于py中缺省参数: 在声明函数的时候对某些参数... 但是,缺省参数必须放在最后,缺省参数的值必须是常量。 举个例子: def sum(a,b): print("%d+%d=%d"%(a,b,a+b)) sum(11,22) sum(13,22) sum(15...
  • 缺省参数缺省参数是声明或定义函数时为函数的参数指定一个默认值,在调用该函数时 ,如果没有指定实参,则采用默认值,否则使用指定的实参。 void Test(int a=0) { cout&lt;&lt;a&lt;&lt;endl; ...
  • 关于py中缺省参数:在声明函数的时候对某些参数(一个或多个)进行赋值,在你调用的时候无需在实参列表中体现该参数,但是在执行的时候会默认加上这个已经在形参中定义好的参数。 但是,缺省参数必须放在最后,缺省...
  • 如果定义了构造函数,则不会生成默认构造函数(不带形式参数的)。拷贝构造函数,如果没有定义;赋值操作符,如果没有定义;默认析构函数,如果没有定义;地址操作符,如果没有定义;从基类派生时,如果运用以上函数...
  • C++ 关于构造函数

    千次阅读 2011-11-25 17:15:49
    每个类都有构造函数。构造函数的主要功能是给对象分配空间、对私有成员数据赋初值。... 1、缺省参数的构造函数中,形参已经有默认值,定义对象是可根据需要使用其缺省值。  例如:  class CText
  • 尽管有很多书中,都有关于默认构造函数的讲解,可是在实际的编程中,究竟有多少人可以清晰的认识到哪些场合中可以调用默认构造函数,哪些场合必须自己定义构造函数了,所以看似简单的概念,能将它充分理解的话,对...
  • 3. 形参的默认值要在函数声明中给出,而不是在定义中给出。理解:1. 图方便;2. 如果一个形参没有给出对应的实参,那是不是之后的形参都不能给出实参?否则编译器怎么知道哪个实参对应哪个形参?3. 方便修改吧。
  • 成员函数与内联函数

    2019-09-21 19:10:41
    成员函数可以设置缺省参数值 D. 成员函数可以是静态的 [A] [解析] 因为成员函数可以在类体内定义,也可以在类体外定义,所以成员函数不一定都是内联函数;成员函数可以重载,可以设置参数的默认值,成员函数可以是...
  • 只能是缺省参数来实现这个功能,定义一个参数的默认值,然后再进行传参,如果定义的参数里面没有传参进去,那程序他会使用它本身的默认值,如果函数有传参进去的话,那么函数会直接调用传进去的参数来执行函数 关于...
  • 3.函数在命名时,形式参数,局部变量,外部参数的同名问题 4.静态变量的真正用法和含义 5.关于return语句的理解 6.一个实例,用于说明其关系。 问题的回答 变量类型 定义位置 作用域 储存位置 解释说明 全局...
  • python学习之函数

    2016-11-23 16:19:57
    一、函数 注:当一个函数名字由两个单词组成的时候 ,第二个单词的首字母大写(统一)  当我们定义一个函数的时候 用def语句  ...二、关于函数缺省参数  注:假如函数是有形参的 ,则可以在声
  • Python 函数2

    2021-03-23 11:52:33
    缺省参数 回忆内置函数print()的用法 可以在定义的时候采用赋值的方式,给予默认值,给定默认值的参数要放在后面 此时如果缺少参数就采用默认参数,如果有输入,就采用输入值 关于实参的写法,可以全部使用位置参数...
  • C++类的析构函数

    2019-04-15 14:58:00
    一个类只有一个析构函数,且无显示的定义,系统会生成一个缺省的析构函数(合成析构函数) 析构函数不能重载,其与构造函数一定是成对出现 作用: 构造函数相反,用于释放对象资源,并销毁非static成员。 程序...
  • C语言函数调用中堆栈知识

    千次阅读 2016-05-30 21:09:19
    简而言之,函数声明顾名思义就是告诉编译器有一个这样的函数,同时告诉编译器它的返回值类型和参数类型(参数缺省表示参数还没具体给出)。函数定义就是定义一个函数要执行那些特定的操作。而函数调用就是让程序在恰
  • 2函数缺省参数默认实参 3函数的哑元参数了解 4内联函数inline 九C的动态内存分配 1回顾C语言中动态内存分配 2C使用newdelete运算符分配内存 十C引用reference重难点 1定义 2常引用 关于左值和右值 1左值可以放在...
  • 关于python的“重载”

    2018-05-03 18:32:00
    首先,关于python和java的区别: 1.Java有是通过方法名和方法列表来定义一...而python是通过缺省参数的形式,来解决方法重载的问题。 所以,从这方面来说,python和java的“重载”的实现,是不同的。 下面引用...
  • ◆如何定义、使用结构◆ 26 ◆如何在任意程序中显示MessageBox◆ 27 ◆如何在程序判断出debug状态◆ 27 ◆当对话框中有一个滚动条是,当滚动滚动条时的消息控制函数◆ 27 ◆将一个CWnd指针转换成一个控件ID(整数)...
  • [C++]关于重载运算符的一些建议

    千次阅读 2016-05-23 00:22:05
    运算符1. 谨慎定义类型转换函数有两种函数允许编译器进行这些...该函数可以是只定义了一个参数,也可以是虽定义了多个参数但第一个参数以后的 所有参数都有缺省值。 第一个例子:class Name { public: Name(const stri
  • 之前用库的时候,一直是看着别人的教程走的,所以少走了很多弯路。。。0 0.这次做博客想试着自己做,不看文档。...关于collection的定义是第三个参数,如果缺省的话,是通过一个函数默认定义的:『When no
  • QT 绘图函数

    2012-12-21 17:16:03
    函数drawPie()的最后两个参数值的单位为一度的十六分之一。 下面的代码是图8.5(c)中绘制贝赛尔曲线的代码: QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing, true); QPainterPath path; ...
  • C++基础知识

    2018-09-02 18:32:22
    缺省函数 如果非要把缺省函数比作一个事物,它更像是一个备胎。...缺省参数不能同时在函数声明和定义中出现,只能二者去一 c.缺省值必须是常量或者是全局变量 d.C语言不支持 函数重载 函数重载,即在同...
  • 输出缺省参数缺省参数概念缺省参数分类全缺省参数缺省参数注意:函数重载总结关于小编 先来几句废话 来自菜鸟的自术:我是学校学习的C++ 而自学的java 也不知道自己咋想的。全心去学习java了,就把学校的C++ 抛掷...

空空如也

空空如也

1 2 3 4 5 ... 8
收藏数 149
精华内容 59
关键字:

关于函数定义缺省参数的