onst形参或非const形参:
指针形参是指向const类型还是非const类型,将影响函数调用所使用的实参,我们既可以用int *也可以用const int *类型的实参调用user_ptr,但仅能将int *类型的实参传递给reset函数,这个差别来源于指针初始化规则,可以将指向const对象的指针初始化为指向非const对象,但是不可以让指向非const对象的指针指向const对象。
Void reset(int *p)
{
}
Void use_ptr(cons tint *p)
{
}
可以用const对象初始化非cosnt对象。
在C语言中,具有const形参或非const形参的函数并无区别。
复制实参的局限性:
复制实参并不是在所有的情况下都适合,不适宜复制实参的情况包括:
当需要在函数中修改实参的值得时候
当需要以大型对象作为实参传递时,对实际的应用而言,复制对象所付出的时间和存储空间代价往往过大。
当没有办法实现对象的复制时。
从C语言背景转到C++的程序员习惯通过传递指针实现对实参的访问,在C++中,使用引用形参更安全更自然。
应该将不需要修改的形参定义为const引用,普通的非const引用的形参在使用时不太灵活,这也的形参既不能用const对象初始化,也不能用字面值初始化或者产生右值表达式实参初始化。
避免复制vector的角度出发,应考虑将形参声明为引用类型,然而,事实上,程序员倾向于通过传递指向容器中需要处理的元素的迭代器来传递容器。
不需要修改数组形参的元素时,函数应该将形参定义为指向const对象的指针。
Void f(const int *)
通过引用传递数组。
如果形参是数组的引用,编译器不会将数组转化为指针,而是传递数组引用本身,在这种情况下,数组大小成为形参和实参类型的一部分,编译器检查数组的大小和形参的大小是否匹配。
Void printValues(int (int &arr)[10])
{
Int i, j[2]={0,1};
Int k[10]={0,1,2,3,4,5,6,7,8,9};
printValues(&i); //error
printValues(j); //error
printValues(k); //ok
return 0;
}
这个版本的printvalues只严格的接受含有10个整形值得数组。
多维数组的传递:
Void printvalues(int (matrix *)[10], int rowsize)
或者用数组语法定义多维数组:
Void printvalues(int matrix[][10], int rowsize)
传递给函数的数组的处理:
有三种常见的技巧确保函数的操作不超过数组实参的边界。第一种方法是在数组本身放置一个标记来检测数组的结束。C风格的字符串采用这种方法,它是一种字符数组,并且以空字符null作为结束标记。
第二种方法是传递指向数组的第一个和最后一个元素的下一个位置的指针,这种编程风格由标准库所使用的技术启发而得。
Void printValues(cons tint *beg, cons int *end)
{
While(beg!=end){
Cout<<*beg++<<endl;
}
}
Int main()
{
Int j[2]={0,1};
Printvalues(j,j+2);
}
调用这个版本的函数需要传递两个指针,一个指向要输出的第一个元素,另一个指向最后一个元素的下一个位置。
第三种方法是将第二个参数定义为数组的大小,这种使用方法在C程序和标准化之前的C++程序中十分普遍。
Void printvalues(cons tint ia[],size_t size)
{
For(size_t i=0;i!=size;++i)
Cout<<ia[i]<<endl;
}
Int main()
{
Int j[]={0,1};
Printvalues(j,sizeof(j)/sizeof(*j));
Return 0;
}
函数返回值:
如果返回类型不是引用,在函数调用的地方会将函数返回值复制给临时对象,当函数返回非引用类型时,其返回值既可以是局部对象,也可以是求解表达式的结果。
当函数返回引用时,没有复制返回值,相反,返回的是对象本身,千万不要返回局部对象的引用,在返回引用之前,请自问,这个引用是指向那个在次之前存在的对象?和返回局部对象的引用一样,返回指向局部对象的指针也是错误的。一旦函数结束,局部对象被释放,返回指针就变成了指向不再存在的对象的悬垂指针。
默认实参:
默认实参是通过给形参表中的形参提供明确的初始值来指定的,程序员可以为一个或者多个形参定义默认值,但是如果一个形参具有默认实参,那么,它后面的形参都必须具有默认实参。
例如,
String screenInit(string::size_type height=24,string::size_type width=80, char background=’’);
调用包含默认实参的函数时,可以为该形参提供实参,也可以不提供,如果提供了实参,则它覆盖默认的实参值,否则函数将使用默认实参值。
String screen;
Screen=screenInit(); ://equivalent to screenInit(24,80,’ ’)
Screen=screenInit(66); //equivalent to screenInit(66,80, ‘ ’);
Screen=screenInit(66,256); //equivalent to screenInit(66,256,’ ‘)
Screen=screenInit(66,256,’#’);
默认实参只能用来替换函数调用缺少的尾部实参。
使最少使用的默认实参排在最前面,最可能使用的默认实参的形参排在最后。
既可以在函数声明中也可以在函数定义中指定默认实参,但是,在一个文件中,只能为一个形参指定默认实参一次,下面的例子是错误的;
//ff.h
Inf ff(int i=0);
//ff.c
#include “ff.h”
Int ff(int i=0){ } //error
通常,应在函数声明中指定默认实参,并将该声明放在合适的头文件中。
如果在函数定义的形参表中提供默认实参,那么只有在包含该函数定义的源文件中调用该函数时,默认实参才是有效的。、
内联函数可能要在程序中定义不止一次,只要内联函数的定义在某个源文件中只出现一次,而且在所有源文件中,其定义必须是完全相同的,把内联函数的定义放在头文件中,可以确保在调用函数时所使用的定义是相同的,并且保证在调用点该函数的定义对编译器可见。
在解释任何构造函数的定义之前,注意到构造函数时放在类的public部分的,通常构造函数会作为类的接口的一部分。
Sales_item():unis_sold(0),revenue(0.0){}
在冒号和花括号之间的代码称作构造函数的初始化列表。多个成员的初始化用逗号隔开。
如果没有为一个类显示定义任何构造函数,编译器将自动为这个类生成默认构造函数。合成构造函数一般适用于仅包含类类型成员的类,而对于含有内置类型或复合类型的成员类,则通常应该定义他们自己的默认的构造函数初始化这些成员。
重载函数:出现在相同作用域中的两个函数,如果具有相同的名字而形参表不同,则称为重载函数。如果两个函数的形参完全相同,但返回类型不同,则第二个声明是错误的。
参数中有const类型的形参,这种差异不影响传递至函数的对象,复制形参时并不考虑形参是不是const,函数操作的只是副本。但是,形参与const的等价性仅适用于非引用形参,有const引用形参的函数与非const引用的形参的函数时不同的。类似的,函数带有指向const类型的指针的形参与带有指向相同类型的非const对象的指针形参的函数不同。