C++入门(下)--《Hello C++ World!》(2)(C/C++)

06-02 1417阅读

文章目录

  • 前言
  • 引用
    • 临时变量的一个小知识点
    • 引用和指针的区别
    • 内联函数
      • 内联函数
      • 遗忘的补充:宏函数
      • 指针空值---nullptr
      • 作业部分

        前言

        这期会把上一期C++入门没有讲完的引用,内联函数跟nullptr给讲完,下期将会讲解类和对象

        关于 C++的文章通通收录在了这个专栏里面:

        C++入门(下)--《Hello C++ World!》(2)(C/C++)

        引用

        概念:引用是给已存在变量取了一个别名,它和它引用的变量共用同一块内存空间。

        规则:

        1.引用在定义时必须初始化

        2.一个变量可以有多个引用

        3.引用一旦引用一个实体,再不能引用其他实体

        4.引用过程中,权限不可以扩大,只能平移和缩小(权限指的是eg:以后可以既读又改,现在只能读了)

        使用场景:

        1.作为函数的形参

        1.做输出型参数–想里面改变,对应外面也改变的那种

        2.减少拷贝,提高效率–对大对象(参数很多的那种,比如:一万个形参都用引用)或者深拷贝对象有用

        2.作为函数的返回值

        1.减少拷贝,提高效率–对大对象(参数很多的那种,比如:一万个形参都用引用)或者深拷贝对象有用(传值返回用拷贝,传引用返回不要拷贝)

        2.修改返回值/获取返回值

         使用举例:
        int a = 10;
        int& ra = a;
        int* x = &a;
        int*& p = x;
        引用一旦引用一个实体,再不能引用其他实体:举例:
        int a = 10;
        int& ra = a;
        int b = 11;
        ra = b;//这句话是改变ra的值,不是让ra成为b的引用
        一个变量可以有多个引用,举例:
        int a =0;
        int& b = a;
        int& c= b;
        引用过程中,权限不可以扩大,只能平移和缩小,举例:(这个点属于常引用的)
          const int a = 0;
          int& b = a;//这个是不行的,扩大了
        int x = 0;
        int& y = x;
        const int& z = x;//这里是权限缩小
        x++;//这里会间接导致z++,但是z不能直接自己++
        const int& m = 10;这样也是允许的
        但是int& m =10;就不行
        
        引用作函数返回值:
        先举个例:
        非引用:
        int Count()
        {
          int n = 0;
         n++;
        return n;//这个的话是靠创建临时变量(变量占字节小的时候才是用寄存器)去过渡
        }
        引用:
        int& Count()
        {
          int n = 0;
         n++;
        return n;
        }
        int main()
        {
        int ret = Count();
        }
        这样的话,分两种情况:
        1.如果Count函数结束,栈帧销毁,没有清理栈帧的话,那么ret的结果侥幸是正确的
        2.如果Count函数结束,栈帧销毁,清理栈帧,那么ret的结果是随机值
        (特别是Count用了之后还进行了其他操作去用栈帧)
        总结:引用做返回值时,出了函数作用域,对象不在了,就不能用引用返回,还在就可以用引用返回
        eg:
        int& Count()
        {
         static int n = 0;
         n++;
        return n;//这里返回的相当于是n的别名
        }
        int& SLAt(SeqList*ps,int pos)
        {
        return ps->a[pos];
        }
        
        易错点:
        int& Count()
        {
        	int n = 0;
        	n++;
        	// ...
        	return n;
        }
        int main()
        {
        	int ret = Count();//这里的ret是n的拷贝,不是引用,要int& ret = Count();才是
        	return 0;
        }
        

        临时变量的一个小知识点

        编译器为了执行代码搞的大部分临时变量都具有常性

        举例: double dd = 12.21;
             int& i = dd;//这时就会报错
          
            但是
        double dd = 12.21;
        int i1 = dd;
        int& i = i1;//这就不会报错
        int func()
        {
         static int x = 0;
         return x;
        }
        main函数里面int& ret = func();//这样就不行
                 要const int& ret = func();//这样才行
        

        常见情况:

        1.发生类型转换时会产生临时变量来辅助,这时的就具有常性

        2.传值引用返回值返回时也会产生临时变量,也有常性

        3.隐式转换

        (临时变量没有常性的情况以后才会提及)

        引用和指针的区别

        语法层面:

        引用是不开空间,是对a取别名 指针是开空间,存储a的地址

        从底层汇编指令实现的角度来看:引用是类似指针的方式实现的–也就是引用也会开空间

        平时理解:引用的语法理解当不开空间,要是谈到底层的话,那就是会开空间

        引用和指针的不同点:
        1.引用概念上定义一个变量的别名,指针存储一个变量地址。
        2.引用在定义时必须初始化,指针没有要求
        3.引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
        4.没有NULL引用,但有NULL指针
        5.在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
        6.引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
        7.有多级指针,但是没有多级引用
        8.访问实体方式不同,指针需要显式解引用,引用编译器自己处理(底层)
        9.引用比指针使用起来相对更安全
        

        内联函数

        内联函数

        概念:以inline修饰的函数叫做内联函数

        作用:在编译器编译时,会在调用内联函数的地方把内联函数在那展开(不会像自定义函数一样是通过call搞得),这样可以避免函数调用建立栈帧的开销

        适用范围:适用于短小并且频繁调用的函数。

        原因:如果不频繁的话,用的效果不明显;函数比较长的话,在编译过程中的指令会非常长,导致可执行程序的占用内存会很大(现在编译器可以主动避免这个了)

        inline对于编译器只是一个建议,最终是否成为inline,是由编译器自己决定的

        编译器会ban掉的一些函数(不让他成为内联,让他就是普通的函数):

        1.比较长的函数

        2.大多数递归函数

        注意:深度深的递归跟频繁调用的函数是有差别的,不能等同:深度深的递归eg:1000*999一直乘到1这样一下算下来的话会导致文件变得很大,频繁调用函数则不会

        默认debug模式下,inline在编译过程中不会起作用,否则就不方便调试了

        在release版本下才会起作用,但是release版本下又看不了编译,这个需要自己去设置(不同软件的设置方法不一样)

        注意:inline不建议声明和定义分离(要在同一文件下),分离会导致链接错误–原因:inline被展开之后,定义就没有函数地址了,链接就找不到那个函数的地址,就会报错

        内联函数展示:
        inline f(int a)
        {
          return a*10;
        }//也就在前面加个inline就行了
        

        遗忘的补充:宏函数

        优点:不需要建立栈帧,提高调用效率

        缺点:复杂,容易出错;可读性差;不能调试(因为预编译阶段进行了替换)

        C++中替代宏函数的方法:内联函数

        举例:
        正确写法:#define f(x,y) ((x)+(y))
        易错点:1.后面加了;号和return
              2.f(x,y)写成了f(int x, int y)  
              3.x和y没有加()以及大整体没有加括号--出错原因:万一是f(a|b,a&b)
              隐藏知识点:+比&和|的优先级高
        

        指针空值—nullptr

        在后续表示指针空值时建议最好使用nullptr。

        跟用0或者NULL表示指针空值的区别:

        void f(int)
        {
        }
        void f(int*)
        {
        }
        f(0);//会调用第一个--默认情况下0被看作是一个整形常量,不行的话才是(void*)0
        f(NULL);
        //会调用第一个--NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量(具体看编译器是哪种)
        f(nullptr);//会调用第二个
        

        补充知识:像这种形参只给了类型的,没有给名字的也行,如果函数里面没有使用这个形参的话

        作业部分

        引用传值,指针传地址(X)
        原因:引用表面好像是传值,其本质也是传地址
        删除空指针是无害的,但是不能删除引用(对)
        原因:空指针没有任何指向,删除无害,引用是别名,删除引用就删除真实对象
        
        关于c++的inline关键字,以下说法正确的是(BC) 
        A.使用inline关键字的函数会被编译器在调用处展开
        B.头文件中可以包含inline函数的声明
        C.可以在同一个项目的不同源文件内定义函数名相同但实现不同的inline函数
        D.递归函数也都可以成为inline函数
        A:要看编译器有没有把他处理成内联函数
        B:如果头文件里有内联函数的声明和定义的话就行
        
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

相关阅读

目录[+]

取消
微信二维码
微信二维码
支付宝二维码