我是靠谱客的博主 淡淡发夹,最近开发中收集的这篇文章主要介绍C++基础---引用和内联函数的用法引用内联函数,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

引用

引用的概念

引用不是定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它,引用的变量共用同一块内存空间

引用特性

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

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

类似于一个人有多个名字,多个代号

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

引用的应用

引用1:

在数据结构单链表中,尾插函数在不传返回值的情况下,会用到二级指针来传地址来改变地址的值,这里可以用引用,对于不是很理解这里指针的人有很大帮助。

传二级指针是这样的

 

所以改变成引用就是:

 在我们输出型参数也可运用

引用2:引用做返回值

传值返回是这个样子:

int Add(int a, int b)
{
	int c = a + b;
	return c;
}

int main()
{
	int ret = Add(1, 2);
	cout << ret << endl;
	return 0;
}

当我们传引用返回是这个样子:

int& Add(int a, int b)
{
	int c = a + b;
	return c;
}

int main()
{
	int& ret = Add(1, 2);
	cout << ret << endl;
	return 0;
}

传值返回的时候,函数会将数据拷贝到一个临时变量,再将值返回,生成的栈帧也随之销毁。

我们的临时变量存在哪里呢?

  • 如果c比较小(4/8),一般是寄存器充当临时变量,会存到寄存器中
  • 如果c比较大,临时变量放在调用Add函数的栈帧中

 传引用返回将函数名字取一个别名,没有浪费空间,

 当前代码的问题:

  1. 存在非法访问,因为Add(1,2)的返回值是c的引用,所以Add栈销毁了以后,会去访问c位置的空间
  2. 如果Add函数栈帧销毁,清理空间,那么取c值的时候取到的就是随机值,给ret就是随机值,当前这个取决于编译实现了
  3. ps:vs下销毁栈帧,没有清空间数据

 那么什么时候可以用引用返回呢?

如果函数返回时,出了函数作用域,如果返回对象还在(还没还给系统),则可以使用引用返回,如果已经还给系统了,则必须使用传值返回。

int& Count()
{
	static int n = 0;
	n++;
	//...
	return n;
}

引用的价值体现

  1. 做参数
  2. 做返回值

做参数

  • 提高效率(大对象+深层拷贝)
  • 形参修改,可以影响实参(输出型参数)

引用传参

#include <time.h>
struct A
{
	int a[10000];
};

A a;

//值返回--每次拷贝40000byte
A TestFunc1()
{
	return a;
}

//引用返回--没有拷贝
A& TestFunc2()
{
	return a;
}

void TestReturnByReforValue()
{
	//值返回
	size_t begin1 = clock();
	for (size_t i = 0; i < 100000; ++i)
		TestFunc1();
	size_t end1 = clock();

	//引用返回
	size_t begin2 = clock();
	for (size_t i = 0; i < 10000; ++i)
		TestFunc2();
	size_t end2 = clock();

	//计算两个函数运算完成之后的时间
	cout << "TestFunc1 time:" << end1 - begin1 << endl;
	cout << "TestFunc2 time:" << end2 - begin2 << endl;

}

int main()
{
	TestReturnByReforValue();

	return 0;
}

 a作为全局变量,栈帧没有被销毁,所以可以用引用返回

做返回值

  • 提高效率
  • 修改返回变量

a[i]是右值,临时变量具有常行。

ps:表达式的返回值,常量通常可以认为是右值,右值不能修改

所以改为

int& At(int i)

引用读写

使用引用传参,如果函数中不改变参数的值,建议使用const &

void StackPrint(const struct Stack& st){}

const读写权限

 这里还有一个左右值的问题

 结论:const Type& 可以接收各种类型的对象。

引用和指针的不同点:

  1. 引用概念上定义一个变量的别名,指针存著一个变量地址
  2. 引用在定义时必须初始化,指针最好初始化,但是不初始化也不会报错
  3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以再任何时候指向任何一个同类型实体
  4.  没有NULL引用,但有NULL指针
  5. 再sizeof中含义不同:引用结果为引用类型的大小,但指针始终时地址空间所占字节数--32位平台下4个字节
  6. 引用自加引用实体增加1,指针自加即指针向后偏移一个类型的大小
  7. 访问实体方法不同:指针需要显式解引用,引用编译器自己理   
  8. 引用比指针使用起来相对更安全                                                                                         有多级指针,但是没有多级用                                                                                             后面两条总结起来 :指针更复杂一些,更容易出错一些  
    void f1(int* p)
    {
    	*p = 10;
    }
    
    void f2(int& r)
    {
    	r = 10;
    }
    int main()
    {
    //没报错但是调试会崩
    	f1(NULL);
    	f1(0);
    
    	//引用没有初始化,报错
    	/*f2(NULL);
    	f2(0);*/
    	
    	int a = 1;
    	f1(&a);
    	f2(a);
    
    	return 0;
    }

内联函数

调用函数的时候需要建立栈帧,栈帧中要保存一些寄存器,结束后又要恢复,下面的函数可以看到多次调用,多次建立栈帧,有消耗。

可以看到这些都是有消耗的,对于频繁调用小函数,我们是否能优化一下呢?

C语言中提供了宏,宏是一种替换

#define ADD(x,y) ((x)+(y))

cout << ADD(1, 2) << endl;

C++中提供了一个新的方法:内联函数inline

有了inline,我们就不需要用C的宏,因为宏很复杂,很容易出错

概念

以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数压栈的开销,内联函数提升程序运行的效率。

用法

 inline在release中不会建立栈帧,会在调用的地方展开

 我们转到反汇编查看,release中将代码性能优化,以至于优化的太好了,这里面什么都看不到

在Debug中,能看开call函数

call Add函数就代表函数没有展开

那么如何在Debug下面也展开呢?

  • 解决方案资源管理器->右键属性->C/C++->常规->调试信息格式->程序数据库
  • 优化->内联函数扩展->只适用于_inline
  • 应用->确定

 

 此时我们再来调试一下

在Debug下展开成功

特性

  1. inline是一种以空间换时间的做法,省去调用函数的开销。所以代码很长(10行以上)或者有递归的函数不适合使用内联数        所以长函数和递归函数不适合展开 ,调用地方很多,展开后程序可能会一下变得很大           
  2.  inline对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline的函数很长或者是递归函数等等,编译器优化时会忽略掉内联,像第一点提到。
  3. inline不建议声明和定义分离,分离会导致链接错误,因为inline被展开,就没有函数地址了,链接就会找不到  
    // F.h
    #include <iostream>
    using namespace std;
    inline void f(int i);
    // F.cpp
    #include "F.h"
    void f(int i)
    {
    cout << i << endl;
    }
    // main.cpp
    #include "F.h"
    int main()
    {
    f(10);
    return 0;
    }
    f.cpp->f.o符号表中不会生成f函数的地址,因为Inline函数是不需要地址的,都在调用的地方展开了

// 链接错误:main.obj : error LNK2019: 无法解析的外部符号 "void __cdecl f(int)" (?
f@@YAXH@Z),该符号在函数 _main 中被引用

结论:短小,频繁调用的函数建议定义成inline

最后

以上就是淡淡发夹为你收集整理的C++基础---引用和内联函数的用法引用内联函数的全部内容,希望文章能够帮你解决C++基础---引用和内联函数的用法引用内联函数所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(36)

评论列表共有 0 条评论

立即
投稿
返回
顶部