C++ Primer 第六章 函数 复习(2)

简介: C++ Primer 第六章 函数 复习

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 用于函数指针类型

相关文章
|
22天前
|
存储 并行计算 前端开发
【C++ 函数 基础教程 第五篇】C++深度解析:函数包裹与异步计算的艺术(二)
【C++ 函数 基础教程 第五篇】C++深度解析:函数包裹与异步计算的艺术
39 1
|
22天前
|
数据安全/隐私保护 C++ 容器
【C++ 函数 基础教程 第五篇】C++深度解析:函数包裹与异步计算的艺术(一)
【C++ 函数 基础教程 第五篇】C++深度解析:函数包裹与异步计算的艺术
46 0
|
24天前
|
存储 安全 算法
【C/C++ 关键字 函数说明符 】C++ noexcept 关键字(指定某个函数不抛出异常)
【C/C++ 关键字 函数说明符 】C++ noexcept 关键字(指定某个函数不抛出异常)
24 0
|
24天前
|
设计模式 算法 安全
【C/C++ 关键字 函数说明符 】C++ final关键字(修饰成员函数无法被子类重写覆盖)
【C/C++ 关键字 函数说明符 】C++ final关键字(修饰成员函数无法被子类重写覆盖)
37 1
|
24天前
|
算法 安全 编译器
【C++ 关键字 override】C++ 重写关键字override(强制编译器检查该函数是否覆盖已存在的虚函数)
【C++ 关键字 override】C++ 重写关键字override(强制编译器检查该函数是否覆盖已存在的虚函数)
24 0
|
17天前
|
存储 安全 编译器
【C++】类的六大默认成员函数及其特性(万字详解)
【C++】类的六大默认成员函数及其特性(万字详解)
32 3
|
19天前
|
安全 程序员 C++
【C++ 基本知识】现代C++内存管理:探究std::make_系列函数的力量
【C++ 基本知识】现代C++内存管理:探究std::make_系列函数的力量
96 0
|
21天前
|
存储 安全 编译器
【C++ 函数设计的艺术】深挖 C++ 函数参数的选择 智能指针与 std::optional:最佳实践与陷阱
【C++ 函数设计的艺术】深挖 C++ 函数参数的选择 智能指针与 std::optional:最佳实践与陷阱
106 0
|
21天前
|
安全 算法 编译器
【C++中的const函数】何时与如何正确声明使用C++ const函数(三)
【C++中的const函数】何时与如何正确声明使用C++ const函数
26 0
|
21天前
|
安全 编译器 Linux
【C++中的const函数】何时与如何正确声明使用C++ const函数(二)
【C++中的const函数】何时与如何正确声明使用C++ const函数
26 0