C++引用下

简介: C++引用下

下面是几个关于引用返回局部变量的代码,我们来进行分析:

int& func()
{
  int n = 0;
  n++;
  return n;
}
int main()
{
  int ret = func();
  cout << ret << endl;
  return 0;
}


这个函数用了引用返回,返回的是n的别名,但是n是局部变量,当函数调用结束后,n所在的空间会被销毁以及归还了它的使用权


所以 ret的值是不确定的

如果func函数结束,栈帧销毁,没有清理栈帧,ret的值是侥幸正确的

如果func函数结束,栈帧销毁,清理了栈帧,ret的值是随机的


下面再看一个错误案例:


int& func()
{
  int n = 0;
  n++;
  return n;
}
int main()
{
  int& ret = func();
  cout << ret << endl;
  return 0;
}

1

这里ret就是n的别名,函数调用完毕后,函数被销毁,n的空间就被归还给操作系统了

而这里ret还指向原先n的空间,如果此时什么函数都不调用,输出的ret还是正确的值,因为原先的栈帧没被其他函数栈帧覆盖

如果调用了其他函数,原先的栈帧被其他的函数栈帧覆盖,此时的ret就是随机值了


结果如下图:


def5e112bbaf428c97acf3110d43ba7b.png



分析完了几个常见的错误,下面来看一下引用返回的效率


struct A { int a[10000]; };
A a;
// 值返回
A Func1() { return a; }
// 引用返回
A& Func2() { return a; }
void Test()
{
  int begin1 = clock();
  for (int i = 0; i < 100000; ++i)
  Func1();
  int end1 = clock();
  int begin2 = clock();
  for (int i = 0; i < 100000; ++i)
  Func2();
  int end2 = clock();
  cout << "值返回" << end1 - begin1 << endl;
  cout << "引用返回" << end2 - begin2 << endl;
}
int main()
{
  Test();
}

d95a419c49024c8984c2ee9c9180a2b8.png



引用返回还有一个作用就是:修改返回值


在C语言实现的顺序表中,我们如果想修改某位置的值,是需要调用函数的,可能是先通过一个查找函数找到要修改值的位置,然后再调用修改函数,对值进行修改


大致的代码如下:


struct SeqList
{
  int* a;
  int size;
  int capacity;
};
int find(SeqList* plist, int x)
{
  for (int i = 0; i < plist->size; i++)
  {
  if (plist->a[i] == x)
    return i;
  }
}
void modify(SeqList* plist, int pos, int x)
{
  plist->a[pos] = x;
}
int main()
{
  SeqList list;
  //查找5所在的下标,然后进行修改
  int pos = find(&list, 5);
  modify(&list, pos, 1);
}


这样需要调用2次函数,不是很方便

而在C++中,可以通过引用返回解决这个问题

因为顺序表一般都是动态开辟在堆区的,所以使用引用返回完全没有问题


struct SeqList
{
  int* a;
  int size;
  int capacity;
};
int& AT(SeqList& list, int x)
{
  for (int i = 0; i < list.size; i++)
  {
    if (list.a[i] == x)
    return list.a[i];
  }
}
int main()
{
  SeqList list;
  AT(list, 5) += 8;//利用引用可以直接修改
}


总结:


引用传参使用于输出类参数,它也可以提高效率,尤其是深拷贝和大对象

引用返回适用于:1.提高效率(深拷贝和大对象) 2.修改返回值


并且

基本任何场景都可以使用引用传参

谨慎使用引用作为返回值。出了作用域,对象还在,可以使用引用返回,否则不可以使用引用返回。


常引用

const int & = a这种引用被const修饰的叫做常引用
const int a = 10;
int &b = a;

1

2

上面的语句编译时会出错,从2种角度可以进行解释:

1.b是a的别名,b的改变会改变a,不符合const的修饰,所以出错

2.引用的过程中,权限不能放大,可以缩小、平移

所以const int a = 10; const int &b = a;才是正确的


根据上面2种解释,我们再来看看接下来的语句:

int a = 10;
const int &b = a;//可以成功编译,这里是权限的缩小
const int a = 10;
const int &b = a;//权限平移
const int& a = 10;//10是常量,所以要用const接受
int a = 10;//错误,权限放大


我们来观看下面的语句:

const int a = 10;

int b = a;

这个是不是权限放大呢?

答案是:不是

这也可以用2种角度解释:

1.b的改变不会影响a

2.这只是一个拷贝,无权限问题


int main()
{
  double a = 1.11;
  int& ra = a;//错误
}


不同类型间转换会产生临时变量,临时变量具有常性,所以应该是:const int& ra = a;


8e24f58ed7cb44b8a107ae811551b86e.png

前面也说过,值返回也会产生一个临时变量,这个临时变量也有常性

所以用引用接受传值值返回要加上const修饰


int test()
{
  static int n = 10;
  return n;
}
int main()
{
  const int& ret = test();
}


8e9cbc3231f94560b2b8aa4dc034907a.png


引用和指针

在语法层面上,引用就是一个别名,它没有独立的空间,和其他引用公用一个空间

在底层实现上实际是有空间的,引用是通过指针来实现的


int main()
{
  int a = 10;
  int& ra = a;
  ra = 20;
  int* pa = &a;
  *pa = 20;
}


我们看一下引用和指针的汇编代码对比:




8c09dc3b52c5443f9ff25504d73d336f.png

对比可以看出:引用和指针在汇编上没有任何区别

这就说明引用是类似指针的方式实现的

这同时也说明了前面提到过的:传引用和传指针效率基本相同


引用和指针的不同:

1.引用概念上定义一个变量的别名,指针是存储一个变量地址

2.引用在定义是必须初始化,指针不必

3.存在空指针,不存在空引用

4.引用在初始化引用了一个实体后,不同再引用其他实体,指针可以在任何时候指向任何一个同类型实体

5.在sizeof的结果不同,引用结果是引用类型的大小,指针始终是4字节(32位)或8字节(64位)

6.引用自加即实体加1,指针自家即指针向后偏移一个类型大小

7.有多级指针,没有多级引用

8、访问实体方式不同:指针需要解引用,引用编译器自己处理


一道面试题:

关于引用以下说法错误的是( )。(阿里巴巴2015笔试题)

A.引用必须初始化,指针不必

B.引用初始化以后不能被改变,指针可以改变所指的对象

C.不存在指向空值的引用,但是存在指向空值的指针

D.一个引用可以看作是某个变量的一个“别名”

E.引用传值,指针传地址

F.函数参数可以声明为引用或指针类型


答案:A

A、B、C、D、F选项都很容易理解

E选项需要说一下:“引用传值,指针传地址”,第一遍读起来感觉没错误,但其实引用表面好像是传值,其本质也是传地址,只是这个工作有编译器来做,所以错误


目录
相关文章
|
3月前
|
存储 安全 C++
C++中的引用和指针:区别与应用
引用和指针在C++中都有其独特的优势和应用场景。引用更适合简洁、安全的代码,而指针提供了更大的灵活性和动态内存管理的能力。在实际编程中,根据需求选择适当的类型,能够编写出高效、可维护的代码。理解并正确使用这两种类型,是掌握C++编程的关键一步。
48 1
|
4月前
|
C++
C++中的const指针与const引用
C++中的const指针与const引用
53 2
|
2月前
|
存储 安全 C++
浅析C++的指针与引用
虽然指针和引用在C++中都用于间接数据访问,但它们各自拥有独特的特性和应用场景。选择使用指针还是引用,主要取决于程序的具体需求,如是否需要动态内存管理,是否希望变量可以重新指向其他对象等。理解这二者的区别,将有助于开发高效、安全的C++程序。
22 3
|
2月前
|
存储 自然语言处理 编译器
【C++入门 三】学习C++缺省参数 | 函数重载 | 引用
【C++入门 三】学习C++缺省参数 | 函数重载 | 引用
|
3月前
|
存储 安全 编译器
【C++航海王:追寻罗杰的编程之路】引用、内联、auto关键字、基于范围的for、指针空值nullptr
【C++航海王:追寻罗杰的编程之路】引用、内联、auto关键字、基于范围的for、指针空值nullptr
52 5
|
3月前
|
C++
C++引用
C++引用
|
3月前
|
存储 安全 编译器
【C++入门】—— C++入门 (中)_引用
【C++入门】—— C++入门 (中)_引用
31 5
|
3月前
|
C语言 C++ 编译器
【C++语言】冲突-C语言:输入输出、缺省参数、引用、内联函数
【C++语言】冲突-C语言:输入输出、缺省参数、引用、内联函数
【C++语言】冲突-C语言:输入输出、缺省参数、引用、内联函数
|
2月前
|
C++
C++基础知识(二:引用和new delete)
引用是C++中的一种复合类型,它是某个已存在变量的别名,也就是说引用不是独立的实体,它只是为已存在的变量取了一个新名字。一旦引用被初始化为某个变量,就不能改变引用到另一个变量。引用的主要用途包括函数参数传递、操作符重载等,它可以避免复制大对象的开销,并且使得代码更加直观易读。