第7章 函数
【45】7.1形参和实参有什么区别?
答:形参在函数定义的形参表中进行定义,是一个变量,其作用域为整个函数。而实参出现在函数调用中,是一个表达式。进行函数调用时,用传递给函数的实参对形参进行初始化。
【46】7.7解释下面两个形参声明的不同之处:
void f(T);
void f(T&);
答:前者声明的事T类型的形参,在f中修改形参的值不会影响调用f时所传递的实参的值;
后者声明的事T类型的引用形参。在f中修改形参的值实际上相当于修改调用f时所传递的实参的值。
【47】7.8什么时候应该将形参定义为引用类型?
答:如果希望通过函数调用修改实参的值,就应当将形参定义为引用类型;
另外,在向函数传递大型对象时,为了避免复制实参以提高效率,以及使用无法复制的类类型(其复制构造函数为private的类类型)作为形参类型时,也应该将形参定义为引用类型。但这时使用形参的目的是为了避免复制实参,所以应该将形参定义为const引用。
【48】7.9下面的程序虽然是合法的,但可用性还不够好,指出并改正该程序的局限。
bool test(string& s){return s.empty();}
答:其局限性在于,此处使用引用形参的唯一目的是避免复制实参,但没有将形参定义为const引用,从而导致不能使用字符串字面值来调用该函数(因为非const引用形参只能与完全相同类型的非const对象关联)。
【49】7.11何时应将引用形参定义为const对象?如果在需要const引用时将形参定义为普通引用,则会出现什么问题?
答:如果使用引用形参的唯一目的是避免复制实参,则应将引用形参定义为const对象。
如果在需要const引用时,将形参定义为普通引用,则会导致不能使用右值和const对象,以及需要进行类型转换的对象来调用该函数,从而不必要地限制了该函数的使用。
【50】7.12什么时候应该使用指针形参,什么时候应该使用引用形参?解释两者的优点和缺点。
答:当函数要处理数组且函数体不依赖于数组的长度时应使用指针形参,其他情况下应使用引用形参。
指针形参的优点是可以明确地表示函数所操纵的是指向数组元素的指针,而不是数组本身,而且可以使用任意长度的实参数组来调用函数;其缺点是函数体不能依赖于数组的长度,否则容易造成数组内存的越界访问,从而产生错误的结果或者导致程序崩溃。
引用形参的优点是在函数体中依赖数组的长度是安全的;其缺点是限制了可以传递的实参数组,只能使用长度匹配的实参数组来调用函数。
【51】7.17什么时候返回引用是正确的?而什么时候返回const引用是正确的?
答:返回指向函数调用之前已存在的对象的引用是正确的;
当不希望返回的对象被修改时,返回const引用是正确的。
【52】7.24如果有的话,指出下面哪些函数声明是错误的?为什么?
a)int ff(int a,int b=0,int c=0);
b)char *init(int ht=24,in wd,char bckgrnd);
答:b)是错误的。因为在形参表中,具有默认实参的形参应该出现在形参表的末尾。
【53】7.29对于下面的声明和定义,你会将哪个放在头文件?哪个放在程序文本文件中呢?为什么?
a)inline bool eq(const BigInt&,const BigInt&){…}
b)void putValues(int *arr,int size);
答:两者都应放在头文件中。
b)是函数声明,适合放在头文件中;
a)虽然是一个函数定义,但这是一个内联函数的定义,也应该放在头文件中。因为内联函数的定义对编译器而言必须是可见的,以便编译器在调用点内联展开该函数的代码,这样一来,仅有函数原型是不够的,而且内联函数有可能在程序中定义不止一次,这时必须保证在所有源文件中,其定义是完全相同的。把内联函数的定义放在头文件中,可以确保在调用函数时所使用的定义是相同的,并且保证在调用点该函数的定义对编译器是可见的。
【54】7.35下面提供了三组函数声明,解释每组中第二个声明的效果,并指出哪些(如果有的话)是不合法的。
a)int calc(int,int);
int calc(constint,const int);
第二个声明将形参定义为const,这是对第一个声明的重复声明,因为对于非引用形参而言,是否定义为const没有本质区别;
b)int get();
double get();
第二个声明与第一个声明的区别仅仅在于返回类型不同,该声明是不合法的,因为不能仅仅基于不同的返回类型而实现函数重载。
c)int *reset(int *);
double*reset(double *);
第二个声明的效果是:声明了一个重载的reset函数。
【55】7.39解释以下每组声明中的第二个函数声明所造成的影响,并指出哪些不合法(如果有的话)
a)int calc(int ,int);
int calc(constint&,const int&);
b)int calc(char*,char*);
int calc(constchar*,const char*);
c)int calc(char*,char*)
int calc(char*const,char*const);
答:a),b)中第二个声明的效果是:声明了一个重载的calc函数。
c)中第二个声明是对第一个声明的重复声明。因为当形参以副本传递时,不能基于形参是否为const来实现函数重载。
【56】7.40下面的函数调用是否合法?如果不合法,请解释原因。
enum Stat{Fail,Pass};
void test(Stat);
test(0);
答:该函数调用不合法。因为函数的形参为枚举类型Stat,函数调用的实参为int类型。枚举类型对象只能用同一枚举类型的另一对象或一个枚举成员进行初始化,因此不能将int类型的实参值传递给枚举类型的形参。