使用函数的小细节

来源:互联网 发布:网络用语橙汁什么意思 编辑:程序博客网 时间:2024/05/02 00:37

函数形参

        建议尽量使用传引用调用,无需修改引用形参的值时就使用常量引用。(传值引用相当于拷贝一份实参的值给形参,在拷贝大的类对象或者容器对象比较低效,甚至有的类类型并不支持拷贝,函数只能通过引用形参访问该类型的对象)把函数不会改变的形参定义成(普通的)引用是一种比较常见的错误,这么做会给函数调用者一种误导,即函数可以修改它的实参的值。此外,使用引用而非常量引用也会极大地限制函数所能接受的实参类型。例如不能把 const 对象,字面值或者需要转换的对象传递给普通的引用形参。

        如同所有的初始化一样,当用实参初始化形参时会忽略顶层 const 。

        有时会遇到不知道应该向函数传递几个实参的情况,如果所有的实参类型相同,可以使用 initializer_list 的标准库类型。initializer_list 是一种模板类型,如同 vector ,需要事先确定其元素类型(使用 尖括号 <>),需要注意的是 initializer_list 对象中的元素永远是常量值。由于 initializer_list 包含 begin 和 end 成员,所有可以使用范围 for 循环来处理其中的元素。 如果实参类型不相同可以使用可变类型模板。

 

函数的返回值

       不要返回局部对象的引用或指针,局部非静态对象的生命周期只到函数体结束之前,函数完成后,它所占用的存储空间也随之释放掉,因此,这意味着局部变量的引用或指针将指向不再有效的区域。

       调用运算符(圆括号())也具有优先级和结合律。调用运算符的优先级和点运算符及箭头运算符相同,并且满足左结合律,因此,如果函数返回指针,引用或类的对象可以直接访问结果对象的成员。

auto sz = shorterString(s1,s2).size();//调用运算符和点运算符的复用,以较短的形式获得较短 string 对象的长度


 

       调用一个返回引用的函数会得到一个左值,其他返回类型得到右值

       C++11 新标准规定可以返回花括号包围的值的列表,此处的列表也可以对表示函数返回值的临时量进行初始化,如果列表为空,则进行值初始化,否则,返回的值由函数的类型决定。

       函数不能返回数组,但是可以返回数组指针,返回数组指针的函数定义比较麻烦。若想定义一个返回数组指针的函数,数组维度必须紧跟在函数形参列表之后。返回数组指针的函数形式如下所示:

Type (*function (parameter_list) )[dimension]//Type---元素类型//dimension---数组大小
 
int (*func(int i))[10];


 

       实例的理解由内到外,(int i)表示首先 func 函数的形参是一个 int 类型的值;(*func(int i))表示函数调用的结果可以执行解引用操作,即函数返回的结果是一个指针;

再看括号右边,表示指针指向的是一个含有10个元素的数组;最后剩下基本数据类型 int ,表示数组元素为int型。

       要想简化返回数组指针函数的定义有两种方法:

              1.使用类型别名

using arrT = int[10];//arrT是一个类型别名,它表示的类型是一个含有10个元素的整形数组arrT* func(int i);      //func函数返回的是指向10个整数数组的指针(把类型别名看成是一个封装好的整体,不能还原带到声明中去理解)


 

               2.C++11新标准可以使用尾置返回类型来定义,尾置返回类型跟在形参列表后并以一个 -> 符号开头。

auto func(int i)->int(*)[10];


 

函数重载

       如果一个作用域内的几个函数名字相同,但形参列表(返回类型不相同然并卵)不相同(无法区分顶层const,但能区分底层const),我们称之为重载(overload)函数。

       在函数重载中总会遇到需要改变对象 const 属性的情况,这时候好基友 const_cast 就出来帮忙了。const_cast 能且仅能该改变对象的底层 const 属性。

const string &shortString(const string &s1,const string &s2){       return s1.size() <= s2.size() ? s1 : s2;}


 

       上例中函数的返回类型和参数类型都为const string 的引用,函数可以使用两个非常量的实参,但返回类型还是const string 的引用。可使用如下重载版本:

string &shorterString(string &s1,string &s2 )//函数重载可以区分底层const,常量引用是底层const{       auto &r = shorterString(const_cast<const string &>s1,const_cast<const string &>s2);//函数返回的是const类型的引用     return const_cast<string &>(r);//去掉 const 属性}


 

       建议将函数声明置于局部作用域之外,如果在内存作用域中声明函数,它将隐藏外层作用域中声明的同名实体。

       在C++语言中,名字查找发生在类型查找之前。

 

默认实参

        在函数的多次调用中,某个实参被赋予了同一个值,此时我们把这个反复出现的值称为函数的默认实参。调用含有默认实参的函数可以包含该实参也可以省略该实参。需要注意的是,一但某个形参被赋予了默认值后,它后面所有的形参都必须有默认值。(当设计那些含有默认实参的函数时,必须合理设置形参的顺序,将那些经常使用默认值的形参出现在后面)。

       通常应该在函数声明中指定默认实参,并将该声明放在合适的头文件中。

       局部变量不能作为默认实参。即使我们在函数内部声明了一个局部变量用来隐藏外部变量,但是该局部变量无法作为默认实参更新对应形参的值。

 

内联函数和 constexpr 函数

       内联函数(inline)指在每个调用点上“内联”的展开(大概是用类似等价表达式的形式替代),以减少调用函数所需的开销。调用函数需要执行一系列工作:调用前要先保存寄存器,并在返回时恢复;可能需要拷贝实参;程序转向一个新的位置继续执行。

       一般来说,内联机制只用于优化一些规模较小,流程直接,频繁调用的函数。在函数最前添加 inline 表示向编译器发出内联请求,编译器可以选择忽略这个请求。

 

       constexpr 函数是指能用于常量表达式的函数。constexpr 函数的返回值类型及所有形参类型都必须是字面值类型,而且函数体重必须有且只有一条return 语句。constexpr 函数被隐式的指定为内联函数。

 

assert 预处理宏和 NDEBUG 预处理变量---调试程序的辅助手段

        assert(expr); 首先对 expr 求值,若为0,assert 输出信息并终止程序的运行,若为1,assert 什么也不做。 assert 宏常用于检查“不能发生”的条件

        定义NDEBUG能避免检查各种条件所需的运行时开销,此时不会执行运行时检查。(例如当定义了NDEBUG之后,assert 将会什么都不做)

        C++编译器为每个函数都定义了一个__func__,它是 const char 的一个静态数组,用于存放函数的名字。

        C++预处理器定义了4个对于程序调试很有用的名字:

                __FILE__    存放文件名的字符串字面值

                __LINE__    存放当前行号的整形字面值

                __TIME__    存放文件编译时间的字符串字面值

                __DATE__    存放文件编译日期的字符串字面值

 

函数匹配

       编译器首先确定候选函数集,然后选出可行匹配(若无可行函数,编译器将报告无匹配函数的错误),最后选出最佳匹配(如果有的话)。在所有可行函数中若有且只有一个函数满足下列条件,则匹配成功:

       1.该函数每个实参的匹配都不亚于其他可行函数需要的匹配。

       2.至少有一个实参的匹配优于其他可行函数提供的匹配。

       若没有任何一个函数脱颖而出,则编译器将报告二义性调用的信息。

       需要内置类型的转换和提升可能在函数匹配时造成二义性调用错误(所有算数类型转换的级别都一样,从int想unsigned int 的转换并不比 int 向 double 的转换级别高)。

void mainip(long);void mainip(float);mainip(3.14);//错误:二义性调用


 

       double 类型既能转换成 float ,也能转换成 long ,因为存在两种可能的转换,所以该调用具有二义性。

       正如之前所说,函数重载可以区分底层 const(引用和指针const),当调用发生时,编译器通过实参是否是常量来决定选择哪个函数。

 

函数指针

       要想声明一个可以指向该函数的指针只需用指针替换函数名即可。

bool (*pf)(const string &,const string &);


 

       类似于数组,当我们把函数名作为一个值使用时,该函数自动的转换为指针。

       指向不同函数类型的指针间不存在类型转换规则。

       若为函数指针赋一个 nullptr 或者一个值为0的整形常量值,表示该指针不指向任何函数。

       当我们通过指针类型来决定选用哪个函数时,重载函数的指针必须与重载函数中的一个精确匹配(指针的类型和函数的返回类型要匹配,形参列表也要匹配)。

       函数可以返回指向函数的指针,定义同返回指向数组的指针的函数类似,都使用尾置返回类型来表示返回类型。

 

 

 

 

 

1 0
原创粉丝点击