一、函数的声明(declaration):
return_typename function_name(typename parameter_name,...) //函数原型(函数头)
{
...; //函数体
return ...;
}
①形参(parameter)与传值调用:
1>形参:
函数原型处的形参,其名称仅相当于占位符。
所以单独声明函数原型的时候也可将变量名省略。
return_typename function_name(typename ,...)
2>传值调用:
仅将实参的值(的拷贝)传递给形参,而非实参地址处的实参本身。
因此传值调用关心的是使用该值得到的结果而不是处理和改变,其不影响原实参在内存中的值。
#include "stdafx.h"
#include<iostream>
void swap(int, int);
int main()
{
//
using std::cout;
using std::endl;
//
int a = 10;
int b = 1;
cout << "before swap:\n";
cout << "a=" << a << endl;
cout << "b=" << b << endl;
cout << "after swap:\n";
swap(a, b);
cout << "a=" << a << endl;
cout << "b=" << b << endl;
//
system("pause");
return 0;
}
void swap(int a, int b)
{
int c = a;
a = b;
b = c;
return;
}
而想要修改实参地址处的实参本身,就必须选择指针和引用作为形参:
#include "stdafx.h"
#include<iostream>
void swap(int* const, int* const); //指针作为形参,相应的函数原型也应修改
int main()
{
//
using std::cout;
using std::endl;
//
int a = 10;
int b = 1;
cout << "before swap:\n";
cout << "a=" << a << endl;
cout << "b=" << b << endl;
cout << "after swap:\n";
swap(&a, &b); //调用时应传递地址
cout << "a=" << a << endl;
cout << "b=" << b << endl;
//
system("pause");
return 0;
}
void swap(int* const a, int* const b)
{
int c = *a; //全部对传递的地址解除引用
*a = *b;
*b = c;
return;
}
#include "stdafx.h"
#include<iostream>
void swap(int& const, int& const); //引用作为形参,相应的函数原型也应修改
int main()
{
//
using std::cout;
using std::endl;
//
int a = 10;
int b = 1;
cout << "before swap:\n";
cout << "a=" << a << endl;
cout << "b=" << b << endl;
cout << "after swap:\n";
swap(a, b); //调用时直接使用变量即可
cout << "a=" << a << endl;
cout << "b=" << b << endl;
//
system("pause");
return 0;
}
void swap(int& const a, int& const b)
{
int c = a;
a = b;
b = c;
return;
}
以上两种方法运行结果相同:
//为尽量减少不必要的错误,形参(尤其是指针或引用)的修改权限应尽可能的低,使用const严格限定其行为
//由于数组名可以近似的看做const指针,(或者说数组本来就是通过指向内存块的指针来调用的)所以数组做形参的任何一种写法都不能实现数组的传值调用。换句话说,当数组作为形参时,使用的一定是其本身而不是其拷贝。
以c-style string为例:
void array_funct(const char[]);
void array_funct(const char*);
两种写法并没有什么区别。
3>参数传递方式的选择:
(1)使用传递的值而不修改,且数据结构本身很小----传值;
(2)数组或内置数据类型----指针;(不修改则const限定)
(3)其余情况一般全部使用引用;(不修改则const限定)
4>参数的默认值:
int function(const char*,int n=1); //当n没有被提供值,则使用默认值1
//默认参数必须出现在形参列表的最右边。//默认参数只写在函数原型中,而函数定义部分不写。
②返回值(return value)与引用:
事实上,通常情况下调用函数得到返回值都是其原本值的拷贝,而不是其本身;
而返回引用类型则使得返回原地址处的值。
int& funct();
所以应积极使用引用作为返回值类型。
1>返回引用类型的原因:①省去了拷贝的开销,效率更高,尤其对于越是大的数据结构。
②左值用法。
//左值(L-value):地址(内存块),如&和*类型;
//右值(R-value):(无址)数据,如字面值和表达式;
int& funct(a)=b; //将b的值写入funct(a)的返回值地址处
2>返回引用类型的注意事项:①由于允许左值用法,可能导致一些不易察觉的书写错误,为减少这种错误应尽可能使用const限 定引用返回值。
②返回的地址应当有效,在函数体中定义的临时变量,不能作为引用类型返回。
int& funct(int& ft)
{
int a;
a=ft;
return a; //a不是有效的引用,其地址处的内存在函数调用结束时已经被释放
}
//但如果创建的临时变量是使用new创建在heap区,那么其引用就可以作为引用类型返回;
被引用的参数可以作为引用类型返回:
int& funct(int& ft)
{
return ft; //指向被引用的实参地址处的值,而这个值长期存在,所以可行
}
二、函数指针:
声明方法:
int funct(int*); //声明一个函数
int (*p_funct)(int*); //声明一个和该函数特征标相同的指针
pf = funct; //将指针指向该函数
//pf = funct; 该句也说明函数名本身就是函数的入口地址,也就不必&funct。
//在函数指针被使用的情况下,函数的形式可能变得非常复杂,调用时书写冗长,此时可使用自动类型推断auto或typedef减少书写量:
const double* funct(const double*,int); //函数声明
const double* (*p_funct)(const double*,int); //指针声明
typedef const double* (*temp)(const double*,int); //将temp作为函数指针const double* (*)(const double*,int)类型的别名
temp funct1=funct; //将函数指针指向函数
三、内联函数:
常规函数在每次调用时通过函数名寻找跳转至函数stack区,并记录下执行完应该返回的地址,调用结束后返回主程序继续执行。
内联(inline)与宏定义有一定相似性,在编译时使用预设的固有函数代码直接展开在每一个调用处,其行为更接近符号。
inline void funct(){...//函数体}; //定义一个内联函数
//内联函数只在标头定义,不将声明与原型分开,行数不宜过多(一般都写在同一行);
//递归函数不能内联;
//内联函数不能进行异常规范声明;
//内联函数节省了调用函数时的跳转开销,但代价是会使程序体积增大,因此不宜用于函数体复杂的(如含有switch、while)函数。
四、函数重载:
重载(overloading)函数调用时以参数列表(特征标)来区分应匹配的原型:
函数重载适合于多个同名函数基本执行同样的任务,但参数类型及所需实现不同的场合。
void funct(int,char*);
void funct(int,int,char*); //参数数目不同
void funct(char,char*); //参数类型不同
①重载的注意事项:
1>返回值不能作为重载依据:
void funct(int,char*);
int* funct(int,char*); //冲突的声明
2>以const限定为依据的重载只对指针(*)和引用(&)类型参数起作用:
#include "stdafx.h"
#include<iostream>
int afunc(const int& );
int afunc(int& );
int main()
{
//
using std::cout;
using std::endl;
//
int a = 1;
cout << "afunc(3)=" << afunc(1) << endl;
cout << "afunc(a)=" << afunc(a) << endl; //const参数重载只能区分 引用& 和 指针* 类型
//
system("pause");
return 0;
}
int afunc(const int& a)
{
return a * 2;
}
int afunc(int& b)
{
return b * 3;
}
指针(*)类型同,不再列举。
3>当没有任何原型与调用处的参数匹配时,将尝试执行转换:
void funct(int,const char*);
void funct(int,int,const char*);
int main()
{
...;
unsigned int a = 2;
funct(a,"fun"); //a将被从unsigned int转换为int,并与原型void funct(int,const char*)匹配
...;
return 0;
}
...
//当存在多个转换途径时,拒绝调用该函数:
void funct(int,const char*);
void funct(double,const char*);
void funct(int,int,const char*);
int main()
{
...;
unsigned int a = 2;
funct(a,"fun"); //转换途径有int和double两种,拒绝该调用
...;
return 0;
}
...