zoukankan      html  css  js  c++  java
  • 引用和内联函数

    一 内联函数

      内联函数是C++为提高程序运行速度所做的一项改进。

      编译过程的最终产品是可执行程序(由一组机器语言指令组成)。程序运行时,操作系统将这些指令载入到计算机内存中,因此每条指令都有特定的内存地址。

      常规函数调用过程---程序跳到函数的地址,并在函数结束时返回。即,程序执行到函数调用指令时,将在函数调用后立即存储该指令的内存地址,并将函数参数复制到堆栈,跳到标记函数起点的内存单元,执行函数代码,返回值放入寄存器中,然后跳回到地址被保存的指令处(这与阅读文章时停下来看标注,并在阅读完标注后返回到之前阅读的地方类似)

      内联函数是将相应的代码替换函数调用。优点是内联函数运行速度比常规函数稍快,缺点是占用更多内存。

      要使用内联函数,需要:

      (1)在函数声明前加上关键字inline

      (2)在函数定义前加上关键字inline

    注:内联函数不能递归

      另:内联函数与宏的区别

    内联函数参数传递方式与常规函数一样,宏是简单的文本替换来完成

    例如:

    inline double square(double x) {return x*x; }

    #define SQUARE(X) X*X

    SQUARE(0.5)  //->0.5*0.5

    SQUARE(4.5+7.5) //4.5+7.5*4.5+7.5

    SQUARE(c++) //c++*c++

    二 引用变量

    引用时已定义的变量的别名(如土豆,又叫马铃薯)。引用的主要用途是用作函数的形参。通过引用传递参数,函数会访问使用原始数据,而不是像按值传递一样使用副本。

    (1)创建引用变量

    int rats;
    int & rodents = rats;//rodents为rats的引用,即别名  这两个值表明的是同一个事物,同一块内存同一个地址同一个值

    特点:与const指针比较接近,必须在声明引用变量时进行初始化。一旦与某个变量关联,就将一直效忠与它。

    引用与变量一体,共用同一块内存,看好引用的内存和值即可。数组不能声明引用

    (2)将引用作为函数参数

    引用作为函数参数,原理是将函数中变量名成为调用程序中的变量的别名。

    按值传递
    void sneezy(int x);
    int main()
    {
        int times = 20;//创建times变量,并赋值20
      sneezy(times);
    }

    void sneezy(int x)//创建x变量,将传递来的值赋值给x
    {
      ...
    }
    2个变量,两个名字,x和times除了值相同,完全两个变量,没有任何联系
    按引用传递
    void grumpy(int &x);
    int main()
    {
        int times = 20;//创建times变量,赋值20

      grumpy(times); }

    void grumpy(int &x)//x为times的别名
    {...}
    times和x是同一个变量,只不过有两个名称

    函数调用使用实参初始化形参,因此函数的引用参数被初始化为函数调用传递的实参。

    (3)关于引用的临时变量和const引用

    如果实参和引用参数类型不匹配,C++将生成临时变量。仅当参数为const引用时,C++才允许这样使用不会报错。

    关于临时变量,如果引用参数时const,则下面两种情况下生成临时变量(匿名变量)

           实参的类型正确,但不是左值。

           实参的类型不正确,但可以转换为正确的类型

    double refcube(const double & ra)//仅当为常量引用,可以允许下面的情况,因为不修改ra的值,所以是不是临时变量无所谓
    {
        return ra * ra * ra;
    }
    
    int main()
    {
    
        double side = 3.0;
        double * pd = &side;
        double & rd = side;
        long edge = 5L;
        double lens[4] = {2.0, 5.0, 10.0, 12.0};
        refcube(side);
        refcube(lens[2]);
        refcube(*pd);
        refcube(edge);//生产临时变量
        refcube(7.0);//生产临时变量
        refcube(side+7.0);//生产临时变量
    
        const double & p12 = 0.1+0.2;
        system("pause");
        return 0;
    }

    为什么非const引用不可以这样操作?

    理解如下:

    void swapr(int &a, int &b)
    {
        int temp;
        temp = a;
       a = b;
    b = temp; }

    long a = 3, b = 5;
    swapr(a,b);

    上述实参类型和形参的引用类型不匹配,编译器会创建两个临时int变量,将其初始化为3和5,然后交换临时变量的值,实参的值不变

    一个接受引用参数的函数的目的就是修改实参的值,如果创建临时变量,就阻止了函数的这一作用。

    实际上,对于形参为const引用的C++函数,如果实参不匹配,则其行为类似于按值传递,为确保原始数据不被修改,将使用临时变量来存储值。

    将引用参数声明为常量数据的引用的理由:

    1)使用const可以避免无意中修改数据的编程错误

    2)使用const使函数能够处理const和非const实参,否则将只能接受非const数据

    3)使用const引用能使函数能够正确生成并使用临时变量

    (4)将引用用于结构

    引用非常适合用于结构和类,引用主要是为了用于这些类型,而不是基本的内置类型

    struct free_throws
    {
      std::string name;
      int made;
      int attempts;
      float percent;
    }
    free_throws dup;
    free_throws four = {"Whily Looper", 5, 9};
    free_throws five = {"Long Long", 5, 9};
    free_throws & accumulate(free_throws & target, const free_throws & source);
    accumulate(dup, five) = four;//不常用,作为一个例子解释下面问题

    1.函数形参为引用:与按值传递相比,引用传递少了复制拷贝原始结构这一步,可以节省时间和内存。

    2.函数返回值为引用:传统返回机制会计算return后变量(这个值被复制到一个临时位置),并将结果返回给调用函数,

    例:dup = accumulate(four, five);

    如果accumulate返回一个结构,不是引用,会将整个结构复制到一个临时位置,再将这个拷贝复制给dup

    现在是引用,会直接把函数中return的值复制给dup,效率更高

    4.返回引用时应注意:避免返回函数终止时不再存在的内存单元引用。1)返回一个作为参数传递给函数的引用  2)用new来分配新的存储空间,返回指向该空间的指针

    5.可以将const用于引用返回类型,避免返回值被改变

    accumulate(dup, five) = four;可以通过编译,但是若此函数返回值为结构体,则不能通过编译
    原因:
    在赋值语句中,左边必须是可以修改的左值,即左边的子表达式必须标识一个可修改的内存块,此函数返回值为结构体引用时,函数返回dup的引用,它确实标识的时一个这样的内存块,所以
    可以编译通过
    常规(非引用)返回类型是右值---不能通过地址访问的值,这种表达式可以出现在赋值语句的右边,但是不能出现在左边。其他右值包括字面值如10.0和表达式如x+y。获取字面值的地址
    没有意义,但是为何常规函数返回值是右值呢,因为这种返回值位于临时内存单元中,运行到下一条语句时,它们可能就不在存在了。

    (5)将引用用于类对象

    类中有一个特征,基类引用可以指向派生类对象,无需进行强制类型转换。

    可以定义一个接受基类引用作为参数的函数,调用该函数时,可以将基类对象作用参数,也可以将派生类对象作为参数。

      

  • 相关阅读:
    对NumPy中dot()函数的理解
    使用Boostrap框架写一个登录注册界面
    两种方法实现asp.net方案的前后端数据交互(aspx文件、html+ashx+ajax)
    将包含经纬度点位信息的Excel表格数据导入到ArcMap中并输出成shapefile
    [ArcGIS API for JavaScript 4.8] Sample Code-Popups-1-popupTemplate的概念和popup中属性字段值的多种表现形式
    [python爬虫]Requests-BeautifulSoup-Re库方案--robots协议与Requests库实战
    [python爬虫]Requests-BeautifulSoup-Re库方案--Requests库介绍
    [ArcGIS API for JavaScript 4.8] Sample Code-Get Started-widgets简介
    [ArcGIS API for JavaScript 4.8] Sample Code-Get Started-popups简介
    [ArcGIS API for JavaScript 4.8] Sample Code-Get Started-layers简介
  • 原文地址:https://www.cnblogs.com/deerfig/p/11906916.html
Copyright © 2011-2022 走看看