前面学习了一些函数基本使用,下面更深入的学习函数有关的内容。
内联函数:要理解内联函数有普通函数的不同,就应该知道函数的调用过程,下面就分析下函数的调用过程。如:
void fun1() ; void fun2() ; void fun1(){ fun2() ; cout << "fun1 method " << endl ;
} void fun2(){ cout << "fun2 method " << endl ; }
在fun1函数中,当执行到fun2();时,则会跳出fun1函数,找到fun2函数的位置执行,执行完fun2函数再返回fun1函数中fun2的位置继续往下面执行。但内联函数与普通函数不同。如:
void fun1() ; inline void fun2() ; void fun1(){ fun2() ; cout << "fun1 method " << endl ;} inline void fun2(){
cout << "fun2 method " << endl ; }
fun2()为内联函数,则在编译时就会用fun2的函数代码替换fun1中fun2的函数调用,在执行fun1时,无需跳转,代价是占用更多的内存。如果内联函数小,则速度会增快,但如果函数大的话,执行速度会变慢。
内联函数是在函数声明和定义前加inline关键字。 内联函数与宏替换还是不同的,宏替换时文本替换,内联函数不是简单的文本替换。
引用变量:引用是已定义变量的别名(另一个名称),如twain是element变量的别名,则可以通过twain和element来修改element的值。别名的主要作用是作为函数的参数。通过引用函数使用参数的原始数据,而不是实参数的副本。省去了参数的复制等过程。
1:创建引用变量
int rats ;
int & rodents = rats ;
上面就定义了rodents引用变量,rodents和rats使用同一块内存单元,可以通过rodents引用来修改rats的值。
注意:引用在创建时必须进行初始化,初始化后,就不能将该引用变量作为其他变量的引用了。如:
int a = 23 ; int b = 344 ; int &i ; //error 必须初始化 i = age ; //error 初始化后不能再改变了
2:引用作为函数的参数,使函数中的变量名是调用函数中的变量的别名,称为引用传递,可以通过引用修改调用函数中的值。值传递不能修改,要修改液可以使用指针参数。
#include<iostream> using namespace std ; void swapr(int & , int &) ; void swapv(int , int ) ; void swapp(int * , int * ) ; int main(){ int x = 4 ,y = 5 ; swapr(x , y) ; swapv(x , y) ; //值传递 并不能交换两个值 swapp(&x ,&y ) ; return 0 ; } void swapr(int &x ,int & y) { //引用传递 int temp = 0 ; temp = x ; x = y ; y = temp ; } void swapv(int x , int y) { //值传递 int temp = 0 ; temp = x ; x = y ; y = temp ; } void swapp(int *px , int *py) { //使用指针 int *p_temp = px ; px = py ; py = p_temp ; }
使用引用能够修改调用函数的数据,若我们不希望这样呢,可以用const修饰引用形参,如:void cube(const int& x) ;这样我们在cube函数中就不能通过x引用来修改了,另外如果我们不使用const修饰,void fcube(int &x ) ; 我们就不能使用常量参数。
我们尽量应使用const修改参数引用:
1:使用const可以避免无意中修改数据
2:使用const,使函数能够处理const和非const实参,否则只能处理非const参数。
对于使用复合类型(结构、类等)作为参数的函数,我们更应该使用引用了,因为使用引用能够避免对象的复制等操作。
void fun(person p) { } void fun2(person &p ){ }
当调用函数fun时,会调用person类的复制构造函数创建临时变量p, 调用fun2函数时,会创建临时引用,该引用指向实参。
传统返回值和返回引用的区别:
int fun1(int x ) { return x ; } int& fun2(const int &x) { return x ; }
传统返回值与按值传递过程一样,如:int m = fun1(4) ; 过程是将x复制到一个临时位置,然后再复制该变量m。则返回引用是:直接将x复制到m中,省去复制到临时变量中。
我们应避免返回函数中局部变量的引用或指针,因为到函数执行完,局部变量就不存在了,引用或指针的位置也就不存在了。为避免,我们应返回参数的引用或使用new创建指针。
默认参数:我们可以在声明函数时给形参提供默认值,这样我们在调用时,如果我们没有提供实参,就会使用默认值来调用函数。如:
#include<iostream> using namespace std ; const int ArSize = 80 ; char * left(const char* str , int n = 1) ; int main(){ char simple[ArSize] ; cout << "Enter a string : " << endl ; cin.get(simple , ArSize) ; char *pr = left(simple) ; // n 使用默认值 cout << pr << endl ; delete [] pr ; return 0 ; } char * left(const char* str , int n) { if(n < 0 ) n = 0 ; char * pr = new char[n+1] ; int i = 0 ; for( ; i < n && str[i] ; i++ ) { pr[i] = str[i] ; } while(i <= n ) { pr[i] = ' ' ; } return pr ; }
对于带参数列表的函数,必须从右向左添加默认参数,就是说,要为某个参数添加默认参数,则它右边的所有参数都有默认参数。如:
int harpo(int n , int m = 4 , int j = 5) ; //可以使用
int chico(int n , int m = 5 , int j) ; //不可以使用
函数重载:
c++允许定义函数名相同的函数,条件是参数类型或个数不相同。如我们定义计算总值的函数sum:
int sum(int x , int y) { return x + y ; } int sum(float x , float y ) { return x + y ; } int sum(double x , double y ) { return x + y ; }
则上面的三个函数就是重载,编译器将根据传入的参数来决定使用哪个函数。
int cube(int x ) ; int cube(int &x ) ;
void dribble(char * bits ) ;
void dirbble(const char* bits) ;
上面的各对函数不能共存,因为在调用的使用不能区分该调用哪个函数。
函数模板:函数是通用的函数描述,使用泛型来定义函数,其中泛型能够使用类型来进行替换,通过将类型作为参数传递给模板,编译器能够生成该类型的函数。
如:我们要定义交换的函数,可以使用int类型,也可以使用double类型,则我们可以使用函数重载,但这里我们使用函数模板:
#include<iostream> using namespace std ; template<typename T> //函数模板声明 void swap(T& , T& ) ; int main(){ int i = 20 , j = 10 ; cout << "before : i , j = " << i << " , " << j << endl ; swap(i , j ) ; //调用函数模板 cout << "now : i , j = " << i << " , " << j << endl ; double a = 30.0 , b = 40.0 ; cout << "before : a , b = " << a << " , " << b << endl ; swap(a , b) ; //调用函数模板 cout << "now : a , b = " << a << " , " << b << endl ; return 0 ; } template<typename T> //定义函数模板 void swap(T &a , T &b ) { T temp = a ; a = b ; b = temp ; }
函数模板的使用方式同函数相同,在使用前要进行定义、声明。
template<typename T> void swap(T& , T& ) 使用关键字template定义模板 , typename定义类型,可以使用class来替换,T是类型名,类型名可以在函数中使用,作为泛型定义的类型,在使用时会根据实参的类型来创建出特殊的函数。使用时, swap(a , b) ; 编译器将生成double版本:
void swap(double &a , double &b ){ double temp = a ; a = b ; b = temp ; }
函数模板不能缩短可执行程序,如上面,最终将有两个独立的函数定义,像手动定义他们一样,最终的代码不含有任何模板,只包含了为程序生成的实际函数,使用模板的好处是,它是生成多个函数更简单、更可靠。
常用的方式是,将模板定义在头文件中,在使用它的程序中包含该头文件。
模板重载:模板也可以像函数那个重载,只要参数类型或个数不同就行了,如我们要定义一个交换数组的模板,则swap(T *a , T *b , int n ) ; 在函数模板中不是必须使用泛型类型,也可以使用特定的类型。
#include<iostream> using namespace std ; template<typename T> void Swap(T &, T &) ; template<typename T> void Swap(T *, T *, int) ; void show(int[] ,int ) ; int main(){ int i = 10 , j = 20 ; cout << "before : i , j = " << i << " , " << j << endl ; Swap(i , j) ; //调用Swap(int &, int &) ; cout << "now : i , j = " << i << " , " << j << endl ; double a = 20.0 , b = 30.0 ; cout << "before : a , b = " << a << " , " << b << endl ; Swap(a , b) ; //调用Swap(double & , double &) ; cout << "now : a , b = " << a << " , " << b << endl ; int arr1[] = {1, 2, 3, 4, 5} ; int arr2[] = {6, 7, 8, 9, 10} ; cout << "before : " << endl ; show(arr1, 5) ; show(arr2, 5) ; Swap(arr1 , arr2 , 5) ; //调用Swap(int [] , int [] , int ) ; cout << "now : " << endl ; show(arr1, 5) ; show(arr2, 5) ; return 0 ; } template<typename T> void Swap(T &a , T &b) { T temp = a ; a = b ; b = temp ; } template<typename T> void Swap(T *a , T *b , int n ) { T temp ; for(int i = 0 ; i < n ; i++ ) { temp = a[i] ; a[i] = b[i] ; b[i] = temp ; } } void show(int a[] , int n) { for(int i = 0 ; i < n ; i++) { cout << a[i] << " ," ; } cout << endl ; }
显性具体化模板函数:如果存在一个结构体Job,我们使用Swap模板函数,也能够交换两个Job的数据,但是现在我们只希望交换对象的一个元素salary,其他元素不变,则这时Swap模板函数就不行了。我们可以使用具体化模板函数:
#include<iostream> using namespace std ; template<typename T> void Swap(T &a , T &b ); struct Job { char name[40] ; double salary ; int floor ; }; template<> void Swap<Job>(Job &a , Job &b ) ; void Swap(Job &a , Job &b ) ; void show(Job &j) ; int main(){ int i = 20 , j = 40 ; cout << "before : i , j = " << i << " , " << j << endl ; Swap(i , j ) ; cout << "now : i , j = " << i << " , " << j << endl ; Job xu = {"xushuangzhi" , 3500.0 , 4} ; Job sue = {"sue" , 2000.0 , 7} ; cout << "before : " << endl ; show(xu) ; show(sue) ; Swap(xu , sue) ; //这里将调用非模板函数 cout << "now : " << endl ; show(xu) ; show(sue) ; return 0 ; } //函数模板 template<typename T> void Swap(T &a , T &b ){ T temp = a ; a = b ; b = temp ; } //具体化函数模板 template<> void Swap<Job>(Job &a , Job &b ) { double sal_temp = a.salary ; a.salary = b.salary ; b.salary = sal_temp ; int floor_temp = a.floor ; a.floor = b.floor ; b.floor = floor_temp ; } //非模板函数 void Swap(Job &a , Job &b ) { double sal_temp = a.salary ; a.salary = b.salary ; b.salary = sal_temp ; int floor_temp = a.floor ; a.floor = b.floor ; b.floor = floor_temp ; } void show(Job &j) { cout << j.name << " : $ " << j.salary << " on floor :" << j.floor << endl ; }
上面有三个函数都符合Swap(xu , sue)的函数调用,非模板函数,具体化函数模板,常规函数模板,他们的优先级是非模板函数> 具体化函数模板 > 常规函数模板