4.1 概述
4.1.1 功能分解与复合
- 功能分解与功能复合是程序设计的两种手段
- 功能分解:自顶向下(top-down)、逐步精华(step-wise)的设计过程。
- 功能复合:自顶向上(bottom-up)的设计过程。
- 采用过程分解和复合的手段进行程序设计往往要基于一种抽象机制——过程抽象(procedural abstraction)或功能抽象(functional abstraction)
- 控制程序复杂度的一个重要手段是抽象,其中子程序就是一种抽象手段——过程抽象。
- 在C++中,子程序被称为函数
4.1.2 子程序及子程序间的数据传递
- 子程序
- 含义:按代码名来调用和执行的相应代码
- 作用:
- 减少程序中的重复代码,便于维护
- 过程抽象(或功能抽象)
- 实现封装(encapsulation)和信息隐藏(information hiding)
- 为语言功能的扩充提供了支持
- 子程序间的数据传递
- 子程序是从子程序的调用者处获得数据以及把计算结果回送给调用者
- 形式参数(parameter,行参):定义子程序时对它所需的参数和返回值进行说明
- 实在参数(argument,实参):调用子程序时,调用者所提供的数据
- 参数传递方式
- 值传递(call-by-value)(C++默认)
- 把实参数据拷贝给行参,在子程序中通过行参直接访问调用者提供的数据
- 缺点:参数传递的效率不高,需要为行参分配空间和把实参的值复制给行参
- 地址/引用传递(call-by-reference)
- 把实参的地址传给实参,在子程序中通过行参间接访问调用者提供的数据
- 优点:提高参数传递的效率;可用该方式把子程序的执行结果通过参数返回给调用者
- 缺点:子程序间接访问数据,效率下降;子程序可通过行参改变实参的值
- 值传递(call-by-value)(C++默认)
- 子程序是从子程序的调用者处获得数据以及把计算结果回送给调用者
4.2 C++函数
函数(function)是C++实现子程序功能的语言成分
4.2.1 函数的定义
- 格式:<返回值类型> <函数名> (<行参列表>) <函数体>
注意:
- C++的函数可以通过全局变量和指针或引用类型的行参来改变调用者的数据,从而使函数产生副作用
- 在函数体中不能用goto语句转向函数外
- 函数也可以没有返回值,这样的函数往往实现一些输出功能
- 每个C++程序都要定义一个main函数,C++程序的执行从main函数开始,main函数通过返回值把程序的执行情况告诉调用者(通常为操作系统),0表示正常,-1表示异常。
1、编写一个求n!的函数
循环
int Function::factorial(intn){
int sum=1;
for(inti=1;i<=n;i++){
sum=sum*i;
}
return sum;
}
递归
int f(int n){
if(n==0){
return 1;
}else{
return n*f(n-1);
}
}
2、编写求x^n的函数
循环
double Function::power(doublex,intn){
double sum=1;
if(n==0){
sum=0;
}else if(n>0){
for(int i=n;i>0;i--){
sum=x*sum;
}
}else if(n<0){
for(int i=n;i<0;i++){
sum=x*sum;
}
sum=1/sum;
}
return sum;
}
递归
double power(double x,int n){
if(x==0){
return 0;
}
if(n==0){
return 1;
}else if(n>0){
return x*power(x,n-1);
}else{
return 1/power(x,-n);
}
}
3、编写一个函数,输出:优,良,中,及格,不及格
void Function::display_message(doublescore){
if(score>=90){
cout<<"优"<<endl;
}elseif(score>=80){
cout<<"良"<<endl;
}elseif(score>=70){
cout<<"中"<<endl;
}elseif(score>=60){
cout<<"及格"<<endl;
}else{
cout<<"不及格"<<endl;
}
}
4.2.2 函数的调用
函数调用过程
- 计算实参的值
- 把实参分别传递给被调用函数的相应行参
- 执行函数体
- 函数体中执行return语句返回函数调用点,调用点获取返回值(如果有)并执行调用后的操作
- 若有返回值,可把函数作为操作数在表达式中参加运算
- 若无返回值,调用时需加分号(;)构成语句使用
函数声明
- 如果在调用点没有见到被调用函数的定义,则需在调用前对被调用的函数进行声明
- 采用函数原型(function prototype)表示
- <返回值类型><函数名> <(行参列表)> ;
- 作用:给编译器提供信息,对函数调用的合法性进行检查
4.2.3 值作为参数传递
- C++提供两种参数传递方式:值传递(默认)和引用传递
- 值传递中,将实参的值传递给行参,函数体中对行参值的改变不会影响实参的值
4.2.4 局部变量与全局变量
- 根据变量的定义位置,分为局部变量(local variable)和全局变量(global variable)
- 局部变量:在复合语句中定义的变量,函数的行参也可看作局部变量
- 变量的局部性体现了函数的封装性和信息隐蔽的特点
- 全局变量:在函数外部定义的变量,能被程序中所有函数访问
- 全局变量的声明(非定义性声明):extern <类型名> <变量名>
- 全局变量的定义(定义性声明)
- 变量定义与声明的区别:
- 变量定义分配空间,变量声明不用
- 变量定义可初始化,变量声明不可
- 程序中,一个变量可定义一次,可声明多次
- 变量定义与声明的区别:
- 全局变量的好处:实现函数间的数据共享和数据传递
- 全局变量的坏处:破坏函数的独立性,带来安全问题,产生函数副作用(function side-effect)
- 在程序设计中尽量不使用全局变量
- 局部变量:在复合语句中定义的变量,函数的行参也可看作局部变量
4.2.5 基于函数的过程式程序设计
函数是一种过程抽象机制。
4、用函数实现求小于n的所有素数
bool Function::is_prime(intn){
for(inti=2;i<n;++i){
if(n%i==0){
returnfalse;
}
}
returntrue;
}
void Function::print_prime(intn,intcount){
cout<<n<<",";
if(count%6==0){
cout<<endl;
}
}
void Function::show_prime(){
int n,count=1;
cout<<"输出一个正整数:"<<endl;
cin>>n;
if(n<2){
cout<<"输入错误"<<endl;
}else{
cout<<2<<",";
for(inti=3;i<n;i=i+2){
if(Function::is_prime(i)){
count++;
Function::print_prime(i,count);
}
}
cout<<endl;
}
}
4.3 标识符的作用域与变量的生存周期
4.3.1 程序的多模块结构
- 对程序的逻辑单位进行分组体现了程序设计中的模块概念
- 模块(module)划分的基本原则:低耦合,高内聚
- 耦合度(coupling):各模块之间的依赖程度
- 内聚性(cohesion)模块内部各个实体之间的关联程度
- 一个程序模块包含两个部分:接口和实现
- 模块接口(module interface):程序实体和声明,头文件( header file)(以*.h结尾)
- 模块实现(module implementation):模块中程序实体的定义,源文件(source file)(以.cpp结尾)
- 模块的声明:编译预处理命令——文件包含命令(#include)
- 模块main中定义的功能不能被其他模块使用,因此只有一个实现文件main.cpp
4.3.2 命名空间
- 格式:namespace <命名空间名>{
<定义的实体>
<函数>
}
- 使用using<命名空间>,使得相应命名空间的程序实体不必用空间名受限
- C++标准库中的程序实体定义在一个名为std的命名空间中
4.3.3 标识符的作用域
- C++根据标识符的性质以及定义位置,规定了标识符的有效范围——作用域
- 作用域(scope):一个标识符所标识的程序实体在程序中能被访问的程序段
- C++中标识符作用域的分类
- 局部作用域(local scope)
- 在函数中定义或复合语句中,从标识符的定义点开始到函数定义或复合语句结束之间的程序段
- 局部作用域有时是个潜在的作用域(potential scope)
- 如果内层定义同名的不同程序实体,外层定义的标识符应该是从其潜在的作用域扣除内层同名标识符的作用域之后所得到的作用域
- 全局作用域(global scope)
- 构成C++程序的所有模块(源文件),在C++标准中把全局作用域归入连接控制(linkage)的范畴
- 在其它文件中使用全局标识符,要先声明(关键字extern)
- 全局作用域有时是个潜在作用域,在局部作用域中使用与其同名的全局标识符,需用全局解析符(global scope resolution operator)“::“对全局标识符进行修饰
- 用于标识程序中各个模块共享的实体
- 文件作用域(file scope)
- 构成C++程序的某个模块(源文件),只能在定义它们的源文件中访问
- 在全局标识符的定义上加上static关键字,全局标识符就变成文件标识符,只能在定义它们的源文件中使用
- 用const定义的全局常量具有文件作用域,不需在定义时加static
- 用于标识模块内部共享的实体
- 函数原型作用域(function prototype scope)
- 用于函数声明的函数原型,其中行参的作用域是从从函数原型开始到函数原型结束
- 命名空间作用域(namespace scope)
- 当一个命名空间的外部需要使用该命名空间中定义的全局标识符时,需用该命名空间的名字和域解析符(scope resolution operator)“::”来修饰或受限
- 类作用域
- 局部作用域(local scope)
4.3.4 变量的生存期(存储分配)
- 程序中定义的每个变量在程序运行时刻都有与之对应的内存空间,变量占有内存空间的时间段称为该变量的生存期(lifetime)
- C++把变量的生存期分为:静态生存期、自动生存期和动态生存期
- 具有静态生存期的变量,它们的内存空间从程序开始执行就分配,直到程序结束时才收回它们的空间。
- 全局变量具有静态生存期。
- 具有自动生存期的变量,它们的内存空间在程序执行到定义它们的复合语句时才分配,复合语句执行结束后收回。
- 局部变量和函数的参数一般具有自动生存期
- 定义局部变量时可用关键字显示指出生命周期
- auto:自动生存周期(默认)
- static:静态生存周期
- register:自动生存周期,建议编译器将其空间分配到CPU寄存器里
- 自动局部变量可以节省内存
- 具有动态生存期的变量,其内存空间用new操作或调用函数malloc来分配、用delete操作或调用函数free来收回,这样的变量称为动态变量
- 具有静态生存期的变量,它们的内存空间从程序开始执行就分配,直到程序结束时才收回它们的空间。
- 关键字static的含义
- 在全局标识符的定义中,static修饰符用于把全局标识符的作用域改为文件作用域
- 在局部变量的定义中static修饰符用于指出相应的局部变量具有静态生存期,为静态变量,其主要作用是把全局变量的效果限制在某个函数中,stitac存储类使得某些局部变量的值在多个函数调用中得以保留,收到函数封装的保护
- static修饰的函数叫做静态函数,静态函数有两种,根据其出现的地方来分类:
- 如果这个静态函数出现在类里,那么它是一个静态成员函数; 静态成员函数的作用在于:调用这个函数不会访问或者修改任何对象(非static)数据成员。
- 其实很好理解,类的静态成员(变量和方法)属于类本身,在类加载的时候就会分配内存,可以通过类名直接去访问;非静态成员(变量和方法)属于类的对象,所以只有在类的对象产生(创建类的实例)时才会分配内存,然后通过类的对象(实例)去访问。
- 如果它不是出现在类中,那么它是一个普通的全局的静态函数。
- 这样的static函数与普通函数的区别是:用static修饰的函数,限定在本源码文件中,不能被本源码文件以外的代码文件调用。而普通的函数,默认是extern的,也就是说它可以被其它代码文件调用。
- 在函数的返回类型前加上关键字static,函数就被定义成为静态函数。普通函数的定义和声明默认情况下是extern的,但静态函数只是在声明他的文件当中可见,不能被其他文件所用。因此定义静态函数有以下好处:
- 其他文件中可以定义相同名字的函数,不会发生冲突
- 静态函数不能被其他文件所用
- 如果这个静态函数出现在类里,那么它是一个静态成员函数; 静态成员函数的作用在于:调用这个函数不会访问或者修改任何对象(非static)数据成员。
- 操作系统为运行程序分配的内存空间包括:
- 静态数据区(static data):用于全局变量、static局部变量、常量的内存分配
- 代码区(code):存放程序的指令
- 栈区(stack):auto存储类的局部变量、函数的行参、函数调用时的相关信息
- 堆区(heap,或自由存储区,free store):动态变量的内存分配
- 程序实体的生存周期与标识符的关系
- 标识符作用域是一个静态的概念,涉及的是程序的静态文本;生存期是一个动态的概念,描述程序运行时实体存在(占内存)的时间段
- 当执行一个标识符的作用域中的代码时,该标识符所标识的实体一定存在;一个程序实体存在并不意味着程序正在执行该实体的标识符作用域中的代码
4.3.5 基于栈的函数调用的实现
- 函数调用是基于栈(stack)实现的,栈是一种后进先出(Last in First out)的线性数据结构,元素的增删只能在某一端进行
- 函数调用时:调用者在栈中为行参还有函数返回地址分配空间,并将实参的值和调用后的返回地址放入所分配的栈空间中
- 函数调用中:被调用函数在栈中为自动存储类的局部变量分配空间,并从栈中(通过行参)获得调用者提供的数据(实参的值)
- 函数调用结束:被调用函数释放局部变量所占的栈空间,并根据栈中的返回地址返回到调用点(存储返回地址的栈空间被释放);调用者释放行参所占的栈空间,然后继续执行调用之后的操作
- 如果函数的返回值为简单的数据类型,返回值通过CPU寄存器返回;否则,存储到调用者的栈空间中一块临时内存空间中。函数调用时,调用者把这块空间的地址传给被调用者,被调用者通过这个地址存储返回值
- 栈随着函数的调用和返回而不断变化着,被多个函数共享,也对函数调用的深度(嵌套调用)有所限制,不应把需要很大内存空间的变量定义为局部变量
4.4 C++函数的进一步讨论
4.4.1 标准函数库
- C++语言提供一个标准库(stander library),其中定义了一些语言本身没有提供的功能
- 用编译预处理命令“#include”把相应的文件包含进来,在连接(link)时把相应的库代码连接到程序中
- 如果包含的是C++的头文件,应通过命名空间std来使用
4.4.2 内联函数
- 函数调用会使程序的执行效率下降,特别是对一些小函数的频繁调用
- 解决方法:
- 带参数的宏
- 使用“#define”定义
- 将内容替换,避免函数调用所需的开销
- 缺点:
- 有时出现重复计算
- 不进行参数检查和转换
- 不利于一些工具对程序的处理
- 内联函数(inline function)
- 在函数定义的返回值类型前加上关键字inline,把该函数体展开到函数调用点
- 具有宏定义和函数两者的优点
- 注意:
- 编译程序对内联函数的限制(递归函数一般不能作为内联函数来实现)
- 内联函数名具有文件作用域,在一个文件定义的内联函数对于另一个文件是不可见的(为了防止同一内联函数的各个定义之间不一致,往往把内联函数放在某个头文件中,使用时#include包含该头文件)
- 带参数的宏
4.4.3 带默认值的形式参数
- C++允许在定义或声明函数时,为函数的某些参数指定默认值
- 注意:
- 有默认值的行参应处于行参表的右部
- 对参数默认值的指定只在函数声明处有意义
- 在不同的源文件中,对同一函数的声明可以对它的同一参数指定不同的默认值;在同一源文件中,对同一函数的声明只能对它的每个参数指定一次默认值
4.4.4 函数名重载
- 给不同函数取相同的名字的机制称为函数名重载(function overloading),是一种实现多态性的语言机制
- 函数名重载包括:重载函数的定义和对重载函数调用的绑定
- 重载函数的定义
- 在相同的作用域中,可以用同一名字定义多个不同的函数,要求这些函数有不同的参数(参数的个数或类型不同)
- 重载函数调用的绑定
- 确定一个对重载函数的调用对应着那一个重载函数定义的过程称为绑定(binding)
- 在编译时刻由编译程序根据实参与行参的匹配情况来决定
- 优先级排序:
- 精确匹配
- 实参与某个重载函数的行参类型完全一致或通过一些细微转换(trivial conversion)后相同
- 提升匹配
- 对实参进行提升转换,然后用转换后的实参与重载函数的行参进行精确匹配
- 提升转换规则:
- 按整型提升规则提升
- char、signed char、unsigned char、short int、unsigned short int类型,如果int能够表示他们的值,转为int,否则转为unsigned int
- bool型转为int,true为1;false为0
- wchar_t和枚举类型转换为下列按次排序第一能表示所有值的类型:int、unsigned int、long int、unsigned long int
- 把float提升到double或把double提升到long double
- 按整型提升规则提升
- 标准转换(stander conversion)匹配
- 对实参进行标准转换,然后用转换后的实参与重载函数的行参进行精确匹配
- 标准转换规则:
- 任何算术型之间可以互相转换
- 枚举型可转换成任何算术型
- 零可以转换成任何算术型或指针型
- 任何类型的指针可以转换成void*
- 派生类指针可以转换成基类指针
- 每个转换标准都是平等的(1~5优先级相同)
- 自定义转换匹配
- 对实参进行自定义类型转换,然后用转换后的实参与重载函数的行参进行精确匹配
- 精确匹配
- 对于两个或以上参数的重载函数绑定问题,匹配原则:
- 如果存在一个重载函数,它有一个参数与相应的实参最佳匹配,而它的其他参数比其它重载函数与相应的实参有更好或相同的匹配,则绑定到该函数,否则绑定失败
- 使用带默认值的参数能减少函数重载的数量
- 重载函数的定义
4.4.5 匿名函数——λ表达式
- 对于一些临时用一下的函数,C++11提供了一种匿名函数机制——𝛌表达式(lambda expression),可以将函数的定义和使用合二为一
- 𝛌表达式的格式:
[<环境变量使用说明>] <形式参数> -><返回值类型指定> <函数体>
- <环境变量使用说明> :指出函数体中对外层作用域中的自动变量的使用限制
- 空:不能使用外层作用域中的自动变量
- &:按引用方式使用外层作用域中的自动变量(可以改变这些变量的值)
- =:按值方式使用外层作用域中的自动变量(不能改变这些变量的值)
- 也可以单独指定可使用的外层自动变量(变量名前可以加“&”,默认为“=”)
- <形式参数>:指出函数的参数及类型
- <返回值类型>:指出函数的返回值类型
- <函数体>:复合语句
- 𝛌表达式通常用于把一个匿名函数作为参数传递给另一个函数的场合
- 𝛌表达式是通过函数对象来实现的
{
int k,m,n;
[ ] (int x)->int {return x;}//不能使用k,m,n
[&] (int x)->int{k++; m++;n++;return x+k+m+n;}//k,m,n可以被修改
[=] (int x)->int{k++; m++;n++;return x+k+m+n;}//k,m,n不可以被修改
[&,n] (int x)->int{k++; m++;return x+k+m+n;}//n不可以被修改
[=,&n] (int x)->int{n++;return x+k+m+n;}//n可以被修改
[&k,m] (int x)->int{k++;return x+k+m+n;}//只能使用k和m,k可以被修改
[=] {return x+k+m+n;}//没有参数,返回值为int
}
4.5 小结
- 子程序是一段命名的程序代码,它通常完成一个独立的子功能。在程序的其它地方通过子程序的名称来调用。采用子程序除了能减少程序的代码外,其主要作用是实现功能抽象。
- 子程序间的数据传递一般是通过参数和返回值机制来实现。
- 常见的参数传递方式:传值(C++默认参数传递机制)和传地址。
- 在C++中使用函数来表示子程序。函数分为:有返回值的和没有返回值的。
- 一个函数的定义由返回值类型、函数名、行参列表、函数体构成,
- 函数体是一个复合语句,执行由调用者引起,执行结束后返回调用者。
- 在函数体中定义的变量称为局部变量,只能在函数体中使用
- 非静态的局部变量具有自动生存期(复合语句或函数执行期间)
- 在函数外部定义的是全局变量,在所有函数中使用
- 全局变量和静态的局部变量具有静态生存期(整个程序运行期间)
- 在函数体中定义的变量称为局部变量,只能在函数体中使用
- 在调用一个函数之前,如果没有见到该函数的定义,则需要对它进行声明。
- 函数体是一个复合语句,执行由调用者引起,执行结束后返回调用者。
- C++的模块由两个文件构成:头文件(.h)和实现文件(.cpp)
- C++中,根据标识符的性质和定义位置规定了标识符的作用域。
- 作用域分为:全局作用域,文件作用域,局部作用域,函数作用域,函数原型作用域,类作用域,命名空间作用域
- 如果一个函数在其函数体中直接或间接调用自己,则称该函数为递归函数。
- 定义递归函数时,要考虑一般情况和特殊情况
- 内联函数能解决对小函数频繁调用所产生的效率不高问题
- 为函数指定默认值能简化程序的书写,提高函数调用的灵活性
- 函数名重载是程序设计的一种多态机制,他为功能相同而参数类型不同或个数不同的函数的定义和使用带来了方便
- 𝛌表达式可以用来实现匿名函数,简化一些场合下的程序编写
课后习题(仅供参考)
1、子程序的作用
- 减少程序中的重复代码,便于维护
- 过程抽象(或功能抽象)
- 实现封装(encapsulation)和信息隐藏(information hiding)
- 为语言功能的扩充提供了支持
2、局部变量的作用
变量的局部性体现了函数的封装性和信息隐蔽的特点
3、变量生存期的含义和种类
含义:变量占有内存空间的时间段称为该变量的生存期(lifetime)
种类:静态生存期、自动生存期、动态生存期
4、区分标识符的作用域原因?C++标识符的作用域的类型
原因:一方面对标识符的可见性进行限制,另一方面隐含着:作用域不相交的两个标识符(标识着不同实体)可以相同
类型:局部作用域、全局作用域、文件作用域、函数作用域、函数原型作用域、命名空间作用域、类作用域
5、全局标识符和局部标识符的区别
全局标识符:在函数外部定义;在程序任何地方都可以使用
局部标识符:在复合语句中定义;在函数体中使用
6、宏cube1和函数cube2的优缺点
#define cube1(x) ((x)*(x)*(x))
double cube2(double x){return x*x*x;}
宏
优点:避免函数调用开销
缺点:不进行参数类型检查和转换
函数
优点:进行参数类型检查和转换
缺点:函数频繁调用消耗资源
7、编写函数digit(n,k)的值
int test::digit(intn,intk){
for(int i=0;i<k-1;++i){
n=n/10;
}
returnn%10;
}
10、求Hn(x)
//递归
if(n==0){
return1;
}else if(n==1){
return2*x;
}else{
return 2*x*h(n-1,x)-2*(n-1)*h(n-2,x);
}
11、计算ack(m,n)
int Test4::ack(unsignedintm,unsignedintn){
if(m==0){
return n+1;
}else if(n==0){
return ack(m-1,1);
}else{
return ack(m-1,ack(m,n-1));
}
}
12、计算路径
int Test4::path(intn){
if(n==2){
return 1;
}else if(n==3){
return 2;
}else if(n==4){
return 4;
}else{
if(n%2==0){
return path(n-1)+path(n-2)+path(n-3);
}else{
returnp ath(n-1)+path(n-2);
}
}
}
13、计算母牛
int Test4::cow(intn){
if(n<4&&n>0){
return 1;
}else{
return cow(n-3)+cow(n-1);
}
}
4.4.4 函数名重载
- 给不同函数取相同的名字的机制称为函数名重载(function overloading),是一种实现多态性的语言机制
- 函数名重载包括:重载函数的定义和对重载函数调用的绑定
- 重载函数的定义
- 在相同的作用域中,可以用同一名字定义多个不同的函数,要求这些函数有不同的参数(参数的个数或类型不同)
- 重载函数调用的绑定
- 确定一个对重载函数的调用对应着那一个重载函数定义的过程称为绑定(binding)
- 在编译时刻由编译程序根据实参与行参的匹配情况来决定
- 优先级排序:
- 精确匹配
- 实参与某个重载函数的行参类型完全一致或通过一些细微转换(trivial conversion)后相同
- 提升匹配
- 对实参进行提升转换,然后用转换后的实参与重载函数的行参进行精确匹配
- 提升转换规则:
- 按整型提升规则提升
- char、signed char、unsigned char、short int、unsigned short int类型,如果int能够表示他们的值,转为int,否则转为unsigned int
- bool型转为int,true为1;false为0
- wchar_t和枚举类型转换为下列按次排序第一能表示所有值的类型:int、unsigned int、long int、unsigned long int
- 把float提升到double或把double提升到long double
- 按整型提升规则提升
- 标准转换(stander conversion)匹配
- 对实参进行标准转换,然后用转换后的实参与重载函数的行参进行精确匹配
- 标准转换规则:
- 任何算术型之间可以互相转换
- 枚举型可转换成任何算术型
- 零可以转换成任何算术型或指针型
- 任何类型的指针可以转换成void*
- 派生类指针可以转换成基类指针
- 每个转换标准都是平等的(1~5优先级相同)
- 自定义转换匹配
- 对实参进行自定义类型转换,然后用转换后的实参与重载函数的行参进行精确匹配
- 精确匹配
- 对于两个或以上参数的重载函数绑定问题,匹配原则:
- 如果存在一个重载函数,它有一个参数与相应的实参最佳匹配,而它的其他参数比其它重载函数与相应的实参有更好或相同的匹配,则绑定到该函数,否则绑定失败
- 使用带默认值的参数能减少函数重载的数量
- 重载函数的定义
16、内联函数为什么定义到头文件中?
内联函数名具有文件作用域,在一个文件定义的内联函数对于另一个文件是不可见的(为了防止同一内联函数的各个定义之间不一致,往往把内联函数放在某个头文件中,使用时#include包含该头文件)
https://github.com/zzq1996/ProgrameDesign
参考:《程序设计教程:用C++语言编程》 陈家骏,郑滔