引用和内联函数

一 内联函数

  内联函数是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)将引用用于类对象

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

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

 

 

 

 

  

posted @ 2019-11-21 17:22  123默小白  阅读(439)  评论(0编辑  收藏  举报