7.1 概述
7.2 函数原型
7.2.1 函数返回类型
函数类型和内置数组类型不能用做返回类型。
// 非法:数组不能做返回类型
int[10] foo_bar();
类类型和容器类型可以被直接返回
7.2.2 函数参数表
没有任何参数的函数可以用空参数表或含有单个关键字void的参数表来表示
int fork(); // 隐式的void参数表
int fork(void); //等价声明
7.2.3 参数类型检查
C++是一种强类型语言。每个函数调用的实参在编译期间都要经过类型检查。若实参类型与相应的参数类型不匹配,如果有可能,就会应用一个隐式的类型转换。
7.3 参数传递
按值传递是参数传递合理的缺省机制
不适合的情况
·当大型的类对象必须作为参数传递时,对实际的应用程序而言,分配对象并拷贝到栈中的时间和空间开销过大。
·当实参的值必须被修改时。
两种方法,一参数被声明为指针,二参数被声明为引用
7.3.1 引用参数
当参数是引用时,函数接收的是实参的左值而不是值的拷贝。
一,必须将一个参数改变成指针来允许改变实参的值
二,向主调函数返回额外的结果
三,向函数传递大型类对象
对于引用参数,函数可以访问被指定为实参的类对象,而不必在函数的活动记录中拷贝它。
把参数声明为const型的引用,可以避免拷贝用作实参的大型类对象,同时又防止函数修改实参的值。
7.3.2 引用和指针参数的关系
引用必须被初始化为指向一个对象,一旦初始化了,它就不能再指向其他对象,一旦初始化了,它就不能再指向其他对象。
指针可以指向一系列不同的对象也可以什么都不指向。
如果一个参数可能在函数中指向不同的对象,或者这个参数可能不指向任何对象,则必须使用指针参数。
引用参数的一个重要用法是,它允许我们在有效地实现重载操作符的同时,还能保证用法的直观性。
7.3.3 数组参数
在C++中,数组永远不会按值传递,它是传递第一个元素的指针
void putValues(int[10]);被编译器视为 void putValues(int*);
数组的长度与参数声明无关
// 三个等价的putValues()声明
void putValues( int* );
void putValues( int[] );
void putValues( int[ 10 ] );
数组被传递为指针,意味着:
·在被调函数内对参数数组的改变将被应用到数组实参上而不是本地拷贝上。可以通过把参数类型声明为const来表明不希望改变数组元素。
·数组长度不是参数类型的一部分
将参数声明为数组的引用。当参数是一个数组类型的引用时,数组长度成为参数和实参类型的一部分,编译器检查数组实参的长度与在函数参数类型中指定的长度是否匹配。
// 参数为10个int的数组
// parameter is a reference to an array of 10 ints
void putValues(int (&arr)[10]);
int main(){
int i,j[2];
putValues(i); //错误:实参不是10个int的数组
putValues(j); //错误:实参不是10个int的数组
return 0;
}
7.3.4 抽象容器类型参数
容器类型还是声明为引用参数比较好。
引用参数 向函数传递大型类对象
#include <vector>
vector<int>::const_iterator look_up(const vector<int> &vec,int value,int &occurs){
vector<int>::const_iterator res_iter=vec.end();
occurs=0;
for(vector<int>::const_iterator iter=vec.begin();iter!=vec.end();++iter)
if(*iter==value){
if(res_iter==iter)
res_iter=iter;
occurs++;
}
return res_iter;
}
引用参数对操作符号重载非常有意义.
数组参数
#include <iostream>
using namespace std;
const lineLength=12;
void putValues(int *ia,int sz)
{
cout<<"("<<sz<<")<";
for(int i=0;i<sz;++i)
{
if(i%lineLength==0 && i)
cout<<"/n/t";
cout<<ia[i];
if(i%lineLength!=lineLength-1 && i!=sz-1)
cout<<",";
}
cout<<">/n";
}
当参数是一个数组类型的引用时,数组长度成为参数和实参类型的一部分,编译器检查数组实参的长度与在函数参数类型中指定的长度是否匹配.
void putValues(int (&arr)[10]);
int main(){
int i,j[2];
putValues(i);//错
putValues(j);//错,不是10个int的数组
return 0;
}
函数模板是一种"其代码在广泛的不同参数类型上保持不变"的机制.
template <class Type>
void putValues(Type *ia,int sz)
{
}
模板参数 Type 关键字class表示模板参数代表一个类型.
putValues
多维数组的参数类型检查只检查多维数组实参中除了第一维之外的所有维的长度与参数的是否相同
matrix声明成一个二维数组,每行由10个列元素构成,其可以被等价地声明为
int (*matrix)[10] 其括号不能去掉
抽象容器类型参数
#include <iostream>
#include <vector>
using namespace std;
const lineLength=12;
void putValues(vector<int> vec)
{
cout<<"("<<vec.size()<<")<";
for(int i=0;i<vec.size();++i)
{
if(i%lineLength==0 && i)
cout<<"/n/t";
cout<<vec[i];
if(i%lineLength!=lineLength-1 && i!=vec.size()-1)
cout<<",";
}
cout<<">/n";
}
int main(){
int i,j[2];
vector<int> vec1(1);
vec1[0]=i;
putValues(vec1);
vector<int> vec2;
for(int ix=0;ix<sizeof(j)/sizeof(j[0]);++ix)
vec2.push_back(j[ix]);
putValues(vec2);
return 0;
}
void putValues(const vector<int> &)
const的引用
7.3.5 缺省实参
char *screenInit(int height=24,int width=80,char background=' ');
如果提供了实参,则它将覆盖缺省的实参值,否则函数将使用缺省实参值.
函数声明可以为全部或部分参数指定缺省实参,在左边参数的任何缺省实参被提供之前,最右边未初始化参数必须被提供缺省参数.
一个参数只能在一个文件中被指定一次缺省实参.
是最可能使用缺省实参的参数出现在后面.
重新声明函数定义的时候
缺省实参可定义以前为缺省的实参
7.3.6 省略号
用省略号指定函数参数表
它们的出现告诉编译器,当函数被调用时,可以有0个或多个实参,而实参的类型未知.
void foo(parm_list,...);
void foo(...);
参数声明后的逗号是可选的
7.4 返回一个值
如果返回值是一个大型类对象,用引用(或指针)返回类型比按值返回类对象效率要高得多.
返回引用应当注意的两个错误。
1.返回一个指向局部对象的引用。局部对象的生命期随函数的结束而结束。在函数结束后,该引用变成未定义内存的别名。
这种情况下,返回类型应该被声明为非引用类型。
2.函数返回一个左值。对返回值的任何修改都将改变被返回的实际对象。
为防止对引用返回值的无意修改,返回值应该被声明为const
7.6 inline函数
inline函数最好放在头文件中.
7.7 链接指示符:extern "C"
程序员用链接指示符告诉编译器,该函数是用其他的程序设计语言编写的既可以是单一语句(single statement)形式,也可以是复合语句(compound statement)形式
//单一语句形式的链接指示符
extern "C" void exit(int);
//复合语句形式的链接指示符
extern "C" {
int printf(const char* ...);
int scanf(const char* ...);
}
//复合语句形式的链接指示符
extern "C"{
#include<cmath>
}
链接指示符不能出现在函数体中.放在头文件中更合适.
可以用extern "C"链接指示符来使C++函数为C程序可用.
//函数calc()可以被C程序调用
extern "C" double calc(double dparm){/*...*/}
P320练习
7.9 指向函数的指针
7.9.4 函数指针的数组
int (*testCases[10])();
testCases声明为一个拥有10个元素的数组.每个元素都是一个指向函数的函数指针,该函数没有参数,返回类型为int.
7.9.4 函数指针的数组
int (*testCases[10])();
testCases声明为一个拥有10个元素的数组.每个元素都是一个指向函数的函数指针,该函数没有参数,返回类型为int.
//typedef 是声明更易读
typedef int (*PFV)();
PFV testCases[10];
初始化
int lexicoCompare( const string &, const string & );
int sizeCompare( const string &, const string & );
typedef int (*PFI2S) ( const string &, const string & );
PFI2S compareFuncs[2]=
{
lexicoCompare,
sizeCompare
}
也可以声明
PFI2S (*pfCompare)[2]=&compareFuncs;
调用lexicoCompare
//两个等价的调用
pfCompare[0](string1,string2);//编写
((*pfCompare)[0])(string1,string2);//显式
P330 例子