函数声明
由函数返回类型,函数名和参数表构成。这三个元素被称为函数声明[function declaration]或函数原型[function prototype]。一个函数可在一个文件中被声明多次
函数声明不需指定参数的名字 只需要每个参数的类型
int abs( int );
int min( int, int );
int gcd( int, int );
函数声明 以及inline 函数的定义 最好放在头文件中,函数类型和内置数组类型不能用作返同类型,必须返回一个指向数组中元素类型的指针
C++ 中参数传递的缺省初始化方法是把实参的值拷贝到参数的存储区中,这被称为按值传递[pass-by-value] 按值传递时,函数不会访问当前调用的实参,函数处理的值是它本地的拷贝,这些拷贝被存储在运行栈中,因此改变这些值不会影响实参的值。
引用参数
在按值传递的情况下,实参的内容没有被改变。若要实现对实参的实际操作,有两种方法实现。
一种方法是 参数被声明成指针 例如
// pswap()交换 v1和 v2指向的值
void pswap( int *v1, int *v2 ) {
int tmp = *v2;
*v2 = *v1;
*v1 = tmp;
}
main()来调用pswap() 现在程序员必须传递两个对象的地址而不是对象本身
pswap( &i, &j );
第二种方法是把参数声明成引用 例如 swap()可重写如下
// rswap() 交换 v1和 v2 引用的值
void rswap( int &v1, int &v2 ) {
int tmp = v2;
v2 = v1;
v1 = tmp;
}
main() 中rswap()的调用看起来像原来的swap()调用
rswap( i, j );
把参数声明成引用 实际上改变了缺省的按值传递参数的传递机制 在按值传递时 函数操纵的是实参的本地拷贝 当参数是引用时 函数接收的是实参的左值而不是值的拷贝 这意味着函数知道实参在内存中的位置 因而能够改变它的值或取它的地址
关于指针
int *&v1;
应该从右向左读 v1 是一个引用 它引用一个指针 指针指向int 型的对象
参数使用引用与指针的区别
引用必须被初始化为指向一个对象 一旦初始化了 它就不能再指向其他对象 指针可以指向一系列不同的对象也可以什么都不指向
因为指针可能指向一个对象或没有任何对象 所以函数在确定指针实际指向一个有效的对象之前不能安全地解引用[dereference] 一个指针 例如
class X;
void manip( X *px )
{
// 在解引用指针之前确信它非 0
if ( px != 0 )
// 解引用指针
}
另一方面 对于 引用参数函数不需要保证它指向一个对象 引用必须指向一个对象 甚至在我们不希望这样时也是如此 例如
class Type { };
void operate( const Type& p1, const Type& p2 );
int main() {
Type obj1;
// 设置 obj1 为某个值
// 错误 : 引用参数的实参不能为 0
Type obj2 = operate( obj1, 0 );
}
如果一个参数可能在函数中指向不同的对象 或者这个参数可能不指向任何对象 则必须使用指针参数
缺省实参
习惯上 缺省实参在公共头文件包含的函数声明中指定 而不是在函数定义中 如果缺省实参在函数定义的参数表中提供 则缺省实参只能用在包含该函数定义的文本文件的函数调用中
已知下列在头文件ff.h 中声明的函数声明
int ff( int a, int b, int c = 0 ); // ff.h
怎样重新声明ff() 来把缺省实参提供给b 下列语句是错误的 因为它重新指定了c 的缺省实参
#include "ff.h"
int ff( int a, int b = 0, int c = 0 ); // 错误
下列看起来错误的重新声明实际上是 正确的
#include "ff.h"
int ff( int a, int b = 0, int c ); // ok
在ff()的重新声明中 b 是没有缺省实参的最右边参数 因此 缺省实参必须从最右边位 置开始赋值的规则没有被打破 实际上 我们可以再次声明ff()为
#include "ff.h"
int ff( int a, int b = 0, int c ); // ok
int ff( int a = 0, int b, int c ); // ok
缺省实参不一定必须是常量表达式 可以使用任意表达式 例如
int aDefault();
int bDefault( int );
int cDefault( double = 7.8 );
int glob;
int ff( int a = aDefault(), int b = bDefault( glob ), int c = cDefault() );
递归
最里层那个返回值称为回渗[percolate],该值依次成为前面每个调用的返回值。
内联
将语句写成函数有一个严重的缺点 调用函数比直接计算条件操作符要慢得多,不但必须拷贝两个实参 保存机器的寄存器 程序还必须转向一个新位置。
注意inline 指示对编译器来说只是一个建议,编译器可以选择忽略该建议。因为把一个函数声明为inline 函数,并不见得真的适合在调用点上展开。
例如,一个递归函数并不能在调用点完全展开,虽然它的第一个调用可以,一个1200 行的函数也不太可能在调用点展开。一般地,inline 机制用来优化小的,只有几行的,经常被调用的函数 。在抽象数据类的设计中 它对支持信息隐藏起着主要作用