6.3 函数重载
函数重载:函数名相同,但形参列表不同(必须是类型上的不同)
const_cast 和重载
重载与作用域
6.4 默认实参、内联、调试帮助
特殊用途语言特性
默认实参
一旦某个形参被赋予了默认值,那么它后面的所有形参都必须有默认值
typedef string::size_type sz; string screen(sz ht=24,sz wid =80,char backgrnd =''); string window; window = screen(); //等价于 screen(24,80,'') window = screen(66); //等价于 screen(66,80,'') window = screen(66,256); //等价于 screen(66,256,'') window = screen(66,256,'#'); //等价于 screen(66,256,'#') window = screen(,,'?'); //错误,只能省略尾部的实参 //多次声明同一个函数是合法的 string screen2(sz,sz,char =''); string screen(sz,sz,char ='*'); //错误,重复声明 string screen(sz =24,sz =80,char); //正确 //局部变量不能作为默认实参 //除此之外,只要表达式的类型能转换成形参所需的类型,就能作为默认实参 //wd,def和ht的声明必须出现在函数之外 sz wd =80; char def =''; sz ht(); //全局变量 string screen(sz = ht(),sz = wd,char = def); string window = screen(); //调用screen(ht(),80,''); //多次声明同一个函数就是同一个函数的默认参数列表越多 void f2(){ def ='*'; //改变默认实参的值 sz wd =100; //隐藏了外层定义的wd,但没有改变默认值 window = screen(); //调用screen(ht(),80,'*') }
内联函数
在每个调用点上 内联地 展开,避免函数调用的开销
内联只是一个请求,是否内联取决于编译器
inline const string& shorterString(const string& s1,const string& s2){ return s1.size() <= s2.size() ? s1 : s2; } cout << shorterString(s1,s2) << endl; //在编译过程中展开类似于下面的形式 cout << (s1.size() <= s2.size() ? s1 : s2) << endl; //一般用于结构简单,调用频繁的函数
constexpr 函数
能用于常量表达式的函数:函数的返回类型以及所有的形参都是字面值类型
函数体中必须有且只有一条return语句
constexpr 函数被隐式指定为内联函数
constexpr int new_sz() {return 42;}; constexpr int foo = new_sz(); //foo也是常量表达式 //如果arg是常量表达式,则scale(arg)也是常量表达式 constexpr size_t scale(size_t cnt){return new_sz() * cnt;}; int arr[scale(2)]; //正确,scale(2) 是常量表达式 int i =2; int arr[scale(i)]; //错误, scale(i)不是常量表达式
调试帮助
只在开发过程中使用的代码,发布时屏蔽掉 assert 预处理宏(cassert头文件)
//如果表达式为假,assert 输出信息并终止程序执行 //如果表达式为真,assert 什么也不做 assert(word.size() > threshold)
NDEBUG 预处理变量
assert 的行为依赖 NDEBUG 预处理变量的状态,如果定义了 NDEBUG ,则 assert 无效
可用于注释掉调试代码
#define NDEBUG #define <cassert> int main(){ int x=0; assert(x); }
除了用于 assert 外,也可以使用 NDEBUG 编写自己的条件调试代码
void print(const int ia[],size_t size){ #ifndef NDEBUG //__func__是编译器定义的一个局部静态变量,用于存放函数的名字 cerr << __func__ << endl; #endif //... }
6.5 函数匹配、函数指针
函数指针:指针指向的是函数
//比较两个string对象的长度 bool lengthCompare(const string& ,const string&); //该函数的类型是bool(const string& ,const string&) //声明一个可以指向该类型函数的指针,只要用指针替换函数名即可 bool(*pf)(const string& ,const string&); //括号不能少 pf = lengthCompare; pf = &lengthCompare; //等价的赋值语句,取地址符是可选的 //可以直接使用指针函数的指针调用该函数,无需提前解引用 bool b1 = pf("hello","bye"); bool b2 = (*pf)("hello","bye"); //等价的调用 bool b3 = lengthCompare("hello","bye"); string::size_type sumLength(const string& ,const string&); bool cstringCompare(const char*,const char*); pf =0; //正确:pf不指向任何函数 pf = sumLength; //错误,返回类型不匹配 pf = cstringCompare; //错误,参数类型不匹配 pf = lengthCompare; //正确:函数和指针类型精确匹配
重载函数的指针
必须精确匹配
void ff(int *); void ff(unsigned int) void (*pf1)(unsigned int) = ff; //pf1指向ff(unsigned int) void (*pf2)(int) = ff; //错误,没有参数列表匹配 double (*pf3)(int *) = ff; //错误,ff和pf3的返回类型不匹配
函数指针形参
函数不可以作为参数,但是函数指针可以,如果参数类型是函数,则会自动转换为函数指针
//第三个形参是函数类型,它会自动地转换成指向函数的指针 void useBigger(const string& s1,const string& s2, bool pf(const string& ,const string&)); //等价的声明:显式地将形参定义成指向函数的指针 void useBigger(const string& s1,const string& s2, bool (*pf)(const string& ,const string&)); //可以直接把函数作为实参使用,会自动转换为指针 useBigger(s1,s2,lengthCompare); //通过使用类型别名,简化使用函数指针 //Func和Func2是函数类型 typedef bool Func(const string& ,const string&); typedef decltype(lengthCompare) Func2; //等价的类型 //FuncP和FuncP2是指针函数的指针 typedef bool (*FuncP)(const string& ,const string&); typedef decltype(lengthCompare) *FuncP2; //useBigger的等价声明,其中使用了类型别名 void useBigger(const string& ,const string&,Func); void useBigger(const string& ,const string&,FuncP2);
返回指向函数的指针
不能返回函数,但可以返回指向函数的指针
using F = int(int*,int); //F是函数类型,不是指针 using PF = int(*)(int*,int); //PF是指针类型 PF f1(int); //正确,PF是指向函数的指针,f1返回指向函数的指针 F f1(int); //错误,F是函数类型,f1不能返回一个函数 F *f1(int); //正确,显式地指定返回类型是指向函数的指针 //当然,我们也能用新形式声明 int (*f1(int))(int*,int); //使用尾置返回的方式 auto f1(int) -> int(*)(int*,int);
用 auto 和 decltype 用于函数指针类型