zoukankan      html  css  js  c++  java
  • 函数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)的函数调用,非模板函数,具体化函数模板,常规函数模板,他们的优先级是非模板函数> 具体化函数模板 > 常规函数模板

  • 相关阅读:
    LeeCode(两数相加)
    Linux vim中移动显示横线
    JAVA各版本的区别
    LNMP一键包安装完成后的目录结构
    tp6打开和关闭调试的方式
    windows安装Thinkphp6的过程
    Composer 的安装方法(一)
    解决:libsodium-1.0.17安装失败
    有些国内的安卓APP下载不了的解决办法
    Linux 安装时不能下载的问题处理办法
  • 原文地址:https://www.cnblogs.com/taxuewuhen/p/3375299.html
Copyright © 2011-2022 走看看