函数2(五)

前面学习了一些函数基本使用,下面更深入的学习函数有关的内容。

内联函数:要理解内联函数有普通函数的不同,就应该知道函数的调用过程,下面就分析下函数的调用过程。如:

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)的函数调用,非模板函数,具体化函数模板,常规函数模板,他们的优先级是非模板函数> 具体化函数模板 > 常规函数模板

原文地址:https://www.cnblogs.com/taxuewuhen/p/3375299.html