6.2.6含有可变函数的形参:
为了编写能够处理不同数量实参的函数:
第一种方法:
传递一个名为initializer_list的标准库函数
第二种方法:
编写一种特殊的函数,也就是所谓的可变参数模板
C++还有一种特殊的形参类型(即省略符),可以用它传递可变数量的实参。不过这种功能一般只用于与C函数交互的接口程序。
initializer_list:
如果实参数量未知,但是类型相同,即可使用。
void test01(initializer_list<string> il)
{
for (auto beg = il.begin(); beg != il.end(); beg++)
{
cout << *beg << endl;
}
}
int main()
{
initializer_list<string> il{ "hello","world" };
test01(il);
}
initializer_list:内的对象永远都是常量值。
省略符形参:
省略符形参应该仅仅用于C和C++通用的类型。
void foo(...);
void foo(...,parm_list);
使用尾置返回类型:
auto func(int i) -> int(*)[10];
使用decltype:
下面的函数返回一个指针,该指针根据参数i不同指向两个已知数组中的某一个:
int odd[] = {1,3,5,7,9};
int even[] = {0,2,4,6,8};
decltype(odd) *arrPtr(int i)
{
return ( i % 2 ) ? &odd : &even;
}
此处arrPtr使用decltype表示返回类型是个指针,并且该指针所指的对象跟odd一致。
因为odd是数组,所以arrPtr返回一个指向含有5个整数的数组的指针。
注意:decltype并不负责把数组类型转换成对应的指针,所以decltype的结果是个数组,要想表示arrPtr返回指针还必须在函数声明时加一个*符号。
6.5 特殊用途语言特性:
6.5.1 默认实参:
某些函数有这样一种实参,在函数的很多次调用中它们都被赋予一个相同的值,此时,我们把这个反复出现的值称为函数的默认实参。
typedef string::size_type sz;
string screen(sz ht = 24, sz wid = 80, char backgrnd = '');
其中每一个形参都提供了默认形参,一旦某个形参都赋予了默认值,那么后面的所有形参都必须有默认值。
调用规则:
string window;
window = screen(); //等价于调用全部的默认值
window = screen(88); //等价于screen(88,默认值,默认值);
window = screen(88,265); //等价于screen(88,256,默认值);
window = screen(88,256,'#'); //等价于screen(88,256,'#')
//函数调用时实参按照位置解析:
window = screen(,,'$'); //等价于screen(默认值,默认值,'$');
window = screen('$'); //等价于screen(‘$’,默认值,默认值);
默认实参声明:
对于函数的声明来说,通常的习惯是将其放在头文件之中,并且一个函数只声明一次。
但是多次声明同一个参数也是合法的。不过,在给定的作用域中一个形参只能被赋予一次默认实参。就是函数的后续声明只能为之前那些没有默认值的形参添加默认实参,而且该形参右侧的所有形参必须都有默认值。
如果函数含有默认实参,则我们在调用该函数的时候传入的实参数量可能少于它实际使用的实参数量。
6.5.2 内联函数和constexpr函数:
规模小的操作定义成函数的好处:
- 阅读和理解这种小函数比阅读等价的条件表达式容易得多。
- 使用函数可以确保行为的统一,避免同样的操作出现不同的方式去进行。
- 如果要修改计算的过程,去修改函数要比找到先前的条件表达式更加方便的多。
- 函数可以被其他应用重复利用,省去了重新编写的代价。
但是调用函数又比求等价表达式的值要慢一些。
这时候就需要内联函数,可以避免函数调用的开销。
比如:
inline const string& short( const string &s1, const string &s2)
{
return s1.size()<=s2.size() ? s1 : s2;
}
//等价于
cout<<short(s1,s2) <<endl; ====> cout<<s1.size()<=s2.size() ? s1 : s2<<endl;
内联说明只是向编译器发出的一个请求,编译器也可以选择忽略这个请求。
一般来说,内联机制用于优化规模小、流程直接、频繁调用的函数。很多编译器都不支持内联递归函数。
constexpr函数:
constexpr函数是指能用于常量表达式的函数。
函数的返回类型及所有形参的类型都是字面值类型,而且函数体中必须有且只有一条return语句:
constexpr int new_sz() {return 42;}
constexpr int foo = new_sz();
我们把new_sz定义成无参数的constexpr函数,因为编译器能在程序编译时验证new_sz函数返回的是常量表达式。
6.5.3 调试帮助:
assert预处理宏:
assert是一种预处理宏,所谓预处理宏其实就是一个预处理变量,使用一个表达式作为条件:
assert(expr);
首先对expr求值,如果表达式为假(0),那么assert输出信息并且终止程序的执行。如果表达式为真那么assert什么都不做。
assert宏常用于检查“不能发生”的条件。例如一个对输入文本进行操作的程序可能要求所有给定单词的长度都大于某个阈值。此时,程序可以包含一条如下所示的语句:
assert(word.size() > threshold);
NDEBUG预处理变量:(P216)
assert的行为依赖于一个名为NDEBUG的预处理变量的状态。如果定义了NDEBUG,则assert什么也不做,默认状态下没有定义NDEBUG,则assert将执行运行时检查。
6.7函数指针:
函数指针指向的是函数而非对象。
比如:
bool len (const string &,const string &);
//该函数的类型是bool(const string &,const string &);若想声明一个可以指向该函数的指针,只需要用指针替换函数名即可。
bool (*ptr) (const string &,const string &);
//ptr指向一个函数,该函数的参数是两个const string的引用,返回的是bool类型
//指针左右的小括号不能省略!
使用函数指针:
p221
承接上部
ptr = len; //ptr指向名为len的函数
ptr = &len; //等价的赋值语句:取地址符是可选的
此外,我们还能直接使用指向函数的指针调用该函数,无须提前解引用指针:
bool b1 = ptr("hello","goodbye"); //调用len函数
bool b2 = (*ptr)("hello","goodbye"); //一个等价的调用
bool b3 = len("hello","goodbye"); //另一个等价的调用
6.7:函数指针:
函数指针指向的是函数而非对象。
例如:
bool lengthCompare(const string &, const string &);
用函数指针则为:
bool (*pr) (const string &,const string &);
pr指向一个返回值为bool类型,参数为两个const string的引用
当我们把一个函数名作为一个值使用的时候,该函数自动地转化成指针。
例如:
pr = lengthCompare; //pr指向lengthCompare函数
pr = &lengthCompare; //等价的赋值语句,&可选!
还可以使用指针直接调用函数:
bool b1 = pr("hello","world");
bool b1 = (*pr) ("hello","world");
bool b1 = lengthCompare("hello","world");
三个都是等价的调用!
指向不同函数类型的指针间不存在转换规则。返回类型、形参类型都需要匹配。
重载函数的指针:
直接给示例:
void ff(int*);
void ff(unsigned int);
void (*pt) (unsigned int) = ff; //pt指向ff(unsigned int)
double (*pr) (unsigned int) = ff; //返回值不同,错误
void (*pt) (int) = ff; //参数类型不同,错误
函数指针形参:
第三个参数自动地转换成指向函数的指针!
void useBigger(const string &s1,const string &s2,bool pr(const string &,const string &));
等价的声明,显示地将形参定义成指向函数的指针
void useBigger(const string &s1,const string &s2,bool (*pr)(const string &,const string &));
也可以直接把函数作为实参使用
userBigger(s1,s2,lengthCompare); //自动将函数lengthCompare转换成指向该函数的指针
由此也可见直接使用函数指针类型显得繁琐,简化如下:
例子1:
函数类型:
typedef bool Func (const string& , const string&) ;
typedef decltype(lengthCompare) Func2; //等价的类型
例子2:
函数指针:
typedef bool (*FuncP) (const string &, const string &);
typedef decltype(lengthCompare) *FuncP2; //等价的类型
使用:
void useBigger(const string&,const string &,Func); //编译器会自动地将Func表示的函数类型转化为指针
void useBigger(const string&,const string &,FunP2);
将auto和decltype用于函数指针类型:
如果明确知道返回的函数是哪一个,就能使用decltype简化书写函数指针返回类型的过程
例子:
string::size_type sumlength(const string & , const string &);
string::size_type largerlength(const string &,const string &);
decltype(sumlength) *getFunc(const string &); //根据形参的取值,getFunc返回指向sumlength或者largerlength的指针
7.3 类的其他特性:
可变数据成员:
在变量声明前加mutable关键字可以让一个类中某个数据成员即使是在const函数中也能被改变。
当提供一个类内初始值的时候,必须以符号=或者花括号来表示。