函数重载
在实际开发中,有时候需要实现几个功能类似的函数,只是细节有所不同。如交换两个变量的值,但这两种变量可以有多种类型,short, int, float等。在C语言中,必须要设计出不同名的函数,其原型类似于:
void swap1(short *, short *); void swap2(int *, int *); void swap3(float *, float *);
但在C++中,这完全没有必要。C++ 允许多个函数拥有相同的名字,只要它们的参数列表不同就可以,这就是函数重载(Function Overloading)。借助重载,一个函数名可以有多种用途。
函数重载是指在同一作用域内,可以有一组具有相同函数名,不同参数列表的函数,这组函数被称为重载函数。重载函数通常用来命名一组功能相似的函数,这样做减少了函数名的数量,避免了名字空间的污染,对于程序的可读性有很大的好处。
C++进行函数重载的实现原理叫名字改编(name mangling),具体的规则是:
1. 函数名称必须相同 。
2. 参数列表必须不同(参数的类型不同、个数不同、顺序不同)。
3. 函数的返回类型可以相同也可以不相同。
4. 仅仅返回类型不同不足以成为函数的重载。
默认参数
默认参数的目的
C++可以给函数定义默认参数值。通常,调用函数时,要为函数的每个参数给定对应的实参。
void func1(int x, int y); void func1(int x, int y) { cout << "x = " << x << endl; cout << "y = " << y << endl; }
无论何时调用func1函数,都必须要给其传递两个参数。但C++可以给参数定义默认值,如果将func1函数参数中的x定义成默认值0, y定义成默认值0,只需简单的将函数声明改成
void func1(int x = 0, int y = 0);
这样调用时,若不给参数传递实参,则func1函数会按指定的默认值进行工作。允许函数设置默认参数值,是为了让编程简单,让编译器做更多的检查错误工作
默认参数的声明
一般默认参数在函数声明中提供。当一个函数既有声明又有定义时,只需要在其中一个中设置默认值即可。若在定义时而不是在声明时置默认值,那么函数定义一定要在函数的调用之前。因为声明时已经给编译器一个该函数的向导,所以只在定义时设默认值时,编译器只有检查到定义时才知道函数使用了默认值。若先调用后定义,在调用时编译器并不知道哪个参数设了默认值。所以我们通常是将默认值的设置放在声明中而不是定义中。
默认参数的顺序规定
如果一个函数中有多个默认参数,则形参分布中,默认参数应从右至左连续定义。当调用函数时,只能向左匹配参数。如:
void func2(int a = 1, int b, int c = 0, int d);//error void func2(int a, int b, int c = 0, int d = 0);//ok
若给某一参数设置了默认值,那么在参数表中其后所有的参数都必须也设置默认值,否则,由于函数调用时可不列出已设置默认值的参数,编译器无法判断在调用时是否有参数遗漏
默认参数与函数重载
默认参数可将一系列简单的重载函数合成为一个。例如:
void func3(); void func3(int x); void func3(int x, int y); //上面三个函数可以合成下面这一个 void func3(int x = 0, int y = 0);
如果一组重载函数(可能带有默认参数)都允许相同实参个数的调用,将会引起调用的二义性。如下代码就是错误的:
void func4(int); void func4(int x, int y = 0); void func4(int x = 0, int y = 0);
所以在函数重载时,要谨慎使用默认参数。
bool类型
在C++中,还添加了一种基本类型,就是bool类型,用来表示true和false。true和false是字面值,可以通过转换变为int类型,true为1,false为0.
int x = true;// 1 int y = false;// 0
任何数字或指针值都可以隐式转换为bool值。
任何非零值都将转换为true,而零值转换为false.一个bool类型的数据占据的内存空间大小为1
inline函数
在C++中,通常定义以下函数来求取两个整数的最大值
int max(int x, int y) { return x > y ? x : y; }
为这么一个小的操作定义一个函数的好处有:
1.阅读和理解函数 max 的调用,要比读一条等价的条件表达式并解释它的含义要容易得多;
2.如果需要做任何修改,修改函数要比找出并修改每一处等价表达式容易得多;
3.使用函数可以确保统一的行为,每个测试都保证以相同的方式实现;
4.函数可以重用,不必为其他应用程序重写代码。
虽然有这么多好处,但是写成函数有一个潜在的缺点:调用函数比求解等价表达式要慢得多。在大多数的机器上,调用函数都要做很多工作:调用前要先保存寄存器,并在返回时恢复,复制实参,程序还必须转向一个新位置执行。即对于这种简短的语句使用函数开销太大。
在C语言中,我们使用带参数的宏定义这种借助编译器的优化技术来减少程序的执行时间,那么在C++中有没有相同的技术或者更好的实现方法呢?答案是有的,那就是内联(inline)函数。内联函数作为编译器优化手段的一种技术,在降低运行时间上非常有用。
什么是内联函数?
内联函数是C++的增强特性之一,用来降低程序的运行时间。当内联函数收到编译器的指示时,即可发生内联:编译器将使用函数的定义体来替代函数调用语句,这种替代行为发生在编译阶段而非程序运行阶段。
定义函数时,在函数的最前面以关键字“inline”声明函数,即可使函数称为内联声明函数。inline int max(int x, y) { return x > y ? x : y; }
谨慎地使用内联
异常安全
异常是程序在执行期间产生的问题。C++ 异常是指在程序运行时发生的特殊情况,比如尝试除以零的操作。异常提供了一种转移程序控制权的方式。C++ 异常处理涉及到三个关键字:try、catch、throw。
throw: 当问题出现时,程序会抛出一个异常。这是通过使用 throw 关键字来完成的。
try: try 块中的代码标识将被激活的特定异常,它后面通常跟着一个或多个 catch 块。
catch: 在您想要处理问题的地方,通过异常处理程序捕获异常。catch 关键字用于捕获异常。
throw表达式
抛出异常即检测是否产生异常,在C++中,其采用throw语句来实现,如果检测到产生异常,则抛出异常。该语句的格式为:
throw 表达式;
异常是一个表达式,其值的类型可以是基本类型,也可以是类
举个例子:double division(double x, double y) { if(y == 0) throw "Division by zero condition!"; return x / y; }
try-catch语句块
try-catch语句块的语法如下:
try { //语句块 } catch(异常类型) { //具体的异常处理... } ... catch(异常类型) { //具体的异常处理... }
try...catch语句块的catch可以有多个,但至少要有一个。
try...catch语句的执行过程是:
执行 try块中的语句,如果执行的过程中没有异常拋出,那么执行完后就执行最后一个 catch块后面的语句,所有 catch块中的语句都不会被执行;
如果 try块执行的过程中拋出了异常,那么拋出异常后立即跳转到第一个“异常类型”和拋出的异常类型匹配的 catch块中执行(称作异常被该 catch块“捕获”),执行完后再跳转到最后一个 catch块后面继续执行。
举个例子:void test() { double x, y; cin >> x >> y; try { if(0 == y) { throw y; } else { cout << (x / y) << endl; } } catch(double d) { cout << "catch(double)" << endl; } catch(int e) { cout << "catch(int)" << endl; } }