• C/C++(C++重载,默认参数,引用)


    C++重载详解

    重载就是同名而非同义,具体意义根据上下文的语境而言。
    重载规则:

    1,函数名相同。
    2,参数个数不同,参数的类型不同,参数顺序不同,均可构成重载。
    3,返回值类型不同则不可以构成重载。
    函数重载(静多态)

    void print(int a)
    {
        //...
    }
    void print(int a,char b)
    {
        //...
    }
    void print(char a,int a)
    {
        //...
    }
    //根据参数的数序,个数执行上下文。
    

    匹配原则:

    1,严格匹配,找到则调用。
    2,通过隐式转换寻求一个匹配,找到则调用。

    void print(int a)
    {
        printf("void print(int a)
    ");
    }
    void print(char a)
    {
        printf("void print(char a)
    ");
    }
    void print(float a)
    {
        printf("void print(float a)
    ");
    }
    void print(double a)
    {
        printf("void print(double a)
    ");
    }
    int main()
    {
        printf(3.4);//3.4是double类型,如果上述没有double类型,他会转向int,float类型,但是两者同时又的话,会报错产生二义性,不要让计算机去解决一些事情。
        print('a');//如果没有char类型最终会转向int类型。字符是以asscII码存在,也就是int类型。
    }
    

    int 到 long 和 double,double 到 int 和 float 隐式类型转换。遇到这种情型,则会引起二义性。
    如果使用时遇到二义性的,在使用时经过强转即可。

    void print(int a)
    {
        printf("void print(int a)
    ");
    }
    void print(char a)
    {
        printf("void print(char a)
    ");
    }
    void print(float a)
    {
        printf("void print(float a)
    ");
    }
    /*void print(double a)
    {
        printf("void print(double a)
    ");
    }*/
    int main()
    {
        print(3.14);// error: call of overloaded 'print(double)' is ambiguous
        print((int)3.14);
        print((float)3.14);
        /*
        void print(int a)
        void print(float a)
        */
    }
    

    重载的原理

    name mangling(倾轧)

    void print(int a)//print_i
    {
        printf("void print(int a)
    ");
    }
    void print(char a)//print_ic
    {
        printf("void print(char a)
    ");
    }
    void print(float a)//...
    {
        printf("void print(float a)
    ");
    }
    void print(double a)//...
    {
        printf("void print(double a)
    ");
    }
    int main()
    {
        print(3.14);//重载函数之间底层处理技术倾轧,把相同的函数名倾轧成不同的函数名。
        return 0;
    }
    

    声明.h文件

    #ifndef PRING_H
    #define PRING_H
    extern "c"//不倾轧
    {
        void print(int a)
        void print(char a)
        void print(int a,char b)
        void print(char a,int b)
    }
    
    #endif //PRING_H
    
    #include<iostream>
    #include "print.h"
    using namespace std;
    int main()
    {
        int a,char b;
        print(a);
        return 0;
    }
    

    函数的定义部分.cpp文件

    void print(int a)//print_i
    {
        printf("void print(int a)
    ");
    }
    void print(char a)//print_ic
    {
        printf("void print(char a)
    ");
    }
    void print(float a)//...
    {
        printf("void print(float a)
    ");
    }
    void print(double a)//...
    {
        printf("void print(double a)
    ");
    }
    

    要求是每个倾轧时要同时,不同的话则报错。c++要完全兼容C,标准C库是没有倾轧的。但是写头文件时要包含的c++中,c++便宜的时候就会有倾轧的文件,这时候就会链接不上。所以为了连接上在自己写的头文件上要求不要倾轧。extern "c"或者extern "c" {多行的情况}

    操作符重载

    C++认为一切操作符都是函数,函数是可以重载的。=>操作符是可以重载的。(并不是所有的运算符都可以重载)

    struct Complex{
        float a;
        float b;
    }
    int main(){
        Complex aa = {1,2},bb = {2,3};
        Complex cc = aa + bb;//error
    
        return 0;
    }
    

    对操作符进行重载

    struct Complex
    {
        float a;
        float b;
    }
    Complex operator+(Complex a,Complex b)
    {
        Complex c;
        c.a = a.a + b.a;
        c.b = a.b + b.b;
        return c;
    }
    int main()
    {
        Complex aa = {1,2},bb = {2,3};
        Complex cc = aa + bb;//right
        cout<<"cc = "<<"("<<cc.a<<","<<cc.b<<")"<<endl;
        return 0;
    }
    //cc = (3,5),实现了结构体之间的加法,当然也可以用函数实现一个加法的过程
    

    默认参数(default parameters)

    #include<iostream>
    #include<time.h>
    using namespace std;
    void weatherCast(string w = "pm=2.5")//默认参数
    {
        time_t t = time(0);//1970,0:0:0的毫秒数
        char tmp[64];
        strftime(tmp,sizeof(tmp),"%Y/%m/%d %x %A",localtime(&t));
        cout<<tmp<<"today is weather "<<w<<endl;
    }
    int main()
    {
        weatherCast();
        weatherCast("sunshine");
        return 0;
    }
    //2018/03/04 03/04/18 Sundaytoday is weather pm=2.5
    //2018/03/04 03/04/18 Sundaytoday is weather sunshine
    

    多参数之间的默认参数,从右至左默认,中间不能跳跃。
    实参的个数+默认的参数个数 >= 形参的个数

    int volume(int l = 3,int w = 4,int h = 5)
    {
        return l*w*h;
    }
    int main()
    {
        cout<<volume()<<endl;
        cout<<volume(2,2)<<endl;
        cout<<volume(2,2,2)<<endl;
        return 0;
    }
    /*
    60
    20
    8
    */
    

    默认参数与重载的冲突

    void print(int a)
    {
        printf("void print(int a)");
    }
    void print(int a,int b = 100)
    {
        printf("void print(int a,int b = 100)");
    }
    int main()
    {
        print(1,200);
    //void print(int a,int b = 200
        print(1);//报错
        return 0;
    }
    

    一个参数或者是两个参数的形式,重载,默认参数都是可以实现的,但是不可以两者同时存在。(在函数重载实现默认参数时要注意,二义性)

    引用

    变量名,本身是一段内存的引用,即别名(alias)。此处引入的引用,是为己有变量起一个别名。

    int main()
    {
        int a = 500;//变量名的实质是一段内存空间的别名
        int&ra = a;//ra是a的引用
        printf("sizeof(ra) = %d sizeof(a) = %d
    ",sizeof(ra),sizeof(a));
        printf("&a = %p &ra = %p
    ",&a,&ra);
        a = 1000;
        printf("a = %d ra = %d
    ",a,ra);
        ra = 1000;
        printf("a = %d ra = %d
    ",a,ra);
    
        int b = 200;
    //    int&ra = b;//报错
        ra = b;//这是赋值
    
        int&rb = ra;
        printf("a = %d ra = %d ra = %d
    ",a,ra,rb);
        int&rbc = rb;
        printf("a = %d ra = %d ra = %d rbc = %d
    ",a,ra,rb,rbc);
        return 0;
    }
    /*
    sizeof(ra) = 4 sizeof(a) = 4
    &a = 0061fea8 &ra = 0061fea8
    a = 1000 ra = 1000
    a = 1000 ra = 1000
    */
    
    /*
    a = 1000 ra = 1000 ra = 1000
    a = 1000 ra = 1000 ra = 1000 rbc = 1000
    */
    

    1 引用是一种声明关系,声明的时候必须要初始化,引用不开辟空间
    2 此种申明关系,一经声明,不可变更
    3 可以对引用再次引用,多次引用的结果是多个引用指向同一个变量

    规则

    1,引用没有定义,是一种关系型声明。声明它和原有某一变量(实体)的关系。故而类型与原类型保持一致,且不分配内存。与被引用的变量有相同的地址。
    2,声明的时候必须初始化,一经声明,不可变更。
    3,可对引用,再次引用。多次引用的结果,是某一变量具有多个别名。
    4,&符号前有数据类型时,是引用。其它皆为取地址。
    应用场景:

    void swap(int *pa,int *pb)
    {
        *pa ^= *pb;
        *pb ^= *pa;
        *pa ^= *pb;
    }
    void swap1(int &pa,int &pb)//利用引用传参,从c++的角度看没有开辟空间
    {
        &pa ^= &pb;
        &pb ^= &pa;
        &pa ^= &pb;
    }
    int main()
    {
        int a = 3;int b = 5;
        int c = 3;int d = 5;
        swap(a,b);
        cout<<"a = "<<a<<" b = "<<b<<endl;
        swap1(c,d);
        cout<<"c = "<<c<<" d = "<<d<<endl;
        return 0;
    }
    /*
    a = 5 b = 3
    c = 5 d = 3
    */
    

    指针的引用

    void swap(char **pa,char **pb)
    {
        char *t = pa;
        *pa = *pb;
        *pb = t;
    }
    int main()
    {
        char *pa = "chaina";
        char *pb = "america";
        cout<<"pa = "<<pa<<"pb = "<<pb<<endl;
        swap(pa,pb);//上述swap()函数中使用二级指针才可以实现交换。
        cout<<"pa = "<<pa<<"pb = "<<pb<<endl;
        return 0;
    }
    /*
    pa = chainapb = america
    pa = americapb = chaina
    */
    

    指针的引用可以实现平级内的交换,不开辟空间

    void swap(char* &pa,char* &pb)
    {
        char *t = pa;
        pa = pb;
        pb = t;
    }
    int main()
    {
        char *pa = "chaina";
        char *pb = "america";
        cout<<"pa = "<<pa<<"pb = "<<pb<<endl;
        swap(pa,pb);//上述swap()函数中使用二级指针才可以实现交换。
        cout<<"pa = "<<pa<<"pb = "<<pb<<endl;
        return 0;
    }
    /*
    pa = chainapb = america
    pa = americapb = chaina
    */
    

    引用的本质是对指针的再次包装,指针是有引用的,不应该有引用的地址

    引用的提高

    引用的本质是指针,C++对裸露的内存地址(指针)作了一次包装。又取得的指针的优良特性。所以再对引用取地址,建立引用的指针没有意义。

    1 可以定义指针的引用,但不能定义引用的引用

    int *p;
    int * & rp = p;//指针的引用
    
    int a;
    int & ra = a;
    int & & raa = ra;//错误,没有引用的引用
    
    

    2 可以定义指针的指针(二级指针),但不能定义引用的指针。

    int **p;//二级指针
    int a;
    int &ra = a;
    int & * p = &ra;//引用本身就是对指针的包装,再把引用打开包。与设计理念相悖。
    //int * & ==>合法 指针的引用
    //int & * ==>不合法 引用的指针
    

    3,可以定义指针数组,但不能定义引用数组,可以定义数组引用。

    int x,y,x;
    int *p[] = {&x,&y,&z};//指针数组
    
    int & rp[] = {x,y,x};//报错,引用数组,rp首元素,相当于int & *
    
    int arr[] = {1,2,3,4,5};//int * 类型 对他引用就变成int * &,指针的引用
    int * &rarr = arr;//报错,缺大小。
    int (&rarr)[5] = arr;//right
    int [5] &rarr = arr;//报错
    int &rarr [5] = arr;//error,引用的数组
    

    常引用

    const 引用有较多使用。它可以防止对象的值被随意修改。因而具有一些特性。
    (1)const 对象的引用必须是 const 的,将普通引用绑定到 const 对象是不合法的。这个原因比较简单。既然对象是 const 的,表示不能被修改,引用当然也不能修改,必须使用 const 引用。实际上,const int a=1; int &b=a;这种写法是不合法的,编译不过。
    (2)const 引用可使用相关类型的对象(常量,非同类型的变量或表达式)初始化。这个是const 引用与普通引用最大的区别。const int &a=2;是合法的。double x=3.14; const int&b=a;也是合法的。
    const不可以改,也不可通过指针或者引用去改变。
    常引用原理:
    const 引用的目的是,禁止通过修改引用值来改变被引用的对象。const 引用的初始化特性较为微妙

    const int a = 100;//对a取地址是 const int *
    int *p = &a;//c中可以,c++中不可
    const int *p = &a;//C++
    
    const int &ra = a;//必须添加const
    
    
    int a;
    double & ra = a;//报错,类型必须一致
    const double & ra = a;//right
    
    int y;
    const int & ry = y;
    
    
    int a = 200;
    int & ra = a;
    const double & rb = a;//他在底层用一个临时变量交换,double tmp = a;const double &rd = tmp;const修饰无法改变。
    cout<<"a = "<<a<<endl;
    cout<<"ra = "<<ra<<endl;
    cout<<"rb = "<<rb<<endl;
    a = 400;
    cout<<"a = "<<a<<endl;
    cout<<"ra = "<<ra<<endl;
    cout<<"rb = "<<rb<<endl;
    
    cout<<"&a = "<<&a<<endl;
    cout<<"&ra = "<<&ra<<endl;
    cout<<"&rb = "<<&rb<<endl;
    
    /*
    a = 200
    ra = 200
    rb = 200
    
    a = 400
    ra = 400
    rb = 200
    
    &a = 0x61fe8c
    &ra = 0x61fe8c
    &rb = 0x61fe90
    */
    

    尽可能使用 const
    use const whatever possible 原因如下:

    1,使用 const 可以避免无意修改数据的编程错误。
    2,使用 const 可以处理 const 和非 const 实参。否则将只能接受非 const 数据。
    3,使用 const 引用,可使函数能够正确的生成并使用临时变量(如果实参与引用参数不匹配,就会生成临时变量)

    引用和指针实质是一样的

  • 相关阅读:
    幸运的秘密
    125条常见的java面试笔试题大汇总之一
    转身离去
    古怪的问题
    125条常见的java面试笔试题大汇总之五
    125条常见的java面试笔试题大汇总之四
    125条常见的java面试笔试题大汇总之三
    关于PostGreSQL中的存储过程
    关于文件编码
    javascript高级程序设计(2)ECMAScript基础
  • 原文地址:https://www.cnblogs.com/intelwisd/p/8506919.html
Copyright © 2020-2023  润新知