详解C++ 引用

引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量。

C++ 引用 vs 指针

引用很容易与指针混淆,它们之间有三个主要的不同:

  • 不存在空引用。引用必须连接到一块合法的内存。
  • 一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。
  • 引用必须在创建时被初始化。指针可以在任何时间被初始化。

C++ 中创建引用

试想变量名称是变量附属在内存位置中的标签,您可以把引用当成是变量附属在内存位置中的第二个标签。因此,您可以通过原始变量名称或引用来访问变量的内容。例如:

int i = 17;

我们可以为 i 声明引用变量,如下所示:

int& r = i;
double& s = d;

在这些声明中,& 读作引用。因此,第一个声明可以读作 "r 是一个初始化为 i 的整型引用",第二个声明可以读作 "s 是一个初始化为 d 的 double 型引用"。下面的实例使用了 int 和 double 引用:

#include <iostream>

using namespace std;

int main ()
{
  // 声明简单的变量
  int  i;
  double d;

  // 声明引用变量
  int&  r = i;
  double& s = d;

  i = 5;
  cout << "Value of i : " << i << endl;
  cout << "Value of i reference : " << r << endl;

  d = 11.7;
  cout << "Value of d : " << d << endl;
  cout << "Value of d reference : " << s << endl;

  return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

Value of i : 5
Value of i reference : 5
Value of d : 11.7
Value of d reference : 11.7

引用通常用于函数参数列表和函数返回值。下面列出了 C++ 程序员必须清楚的两个与 C++ 引用相关的重要概念:

把引用作为参数 

C++ 支持把引用作为参数传给函数,这比传一般的参数更安全。

#include <iostream>
using namespace std;

// 函数声明
void swap(int& x, int& y);

int main ()
{
  // 局部变量声明
  int a = 100;
  int b = 200;

  cout << "交换前,a 的值:" << a << endl;
  cout << "交换前,b 的值:" << b << endl;

  /* 调用函数来交换值 */
  swap(a, b);

  cout << "交换后,a 的值:" << a << endl;
  cout << "交换后,b 的值:" << b << endl;

  return 0;
}

// 函数定义
void swap(int& x, int& y)
{
  int temp;
  temp = x; /* 保存地址 x 的值 */
  x = y;  /* 把 y 赋值给 x */
  y = temp; /* 把 x 赋值给 y */

  return;
}

当上面的代码被编译和执行时,它会产生下列结果:

交换前,a 的值: 100
交换前,b 的值: 200
交换后,a 的值: 200
交换后,b 的值: 100

把引用作为返回值 

可以从 C++ 函数中返回引用,就像返回其他数据类型一样。通过使用引用来替代指针,会使 C++ 程序更容易阅读和维护。C++ 函数可以返回一个引用,方式与返回一个指针类似。

当函数返回一个引用时,则返回一个指向返回值的隐式指针。这样,函数就可以放在赋值语句的左边。例如,请看下面这个简单的程序:

#include <iostream>

using namespace std;

double vals[] = {10.1, 12.6, 33.1, 24.1, 50.0};

double& setValues( int i )
{
 return vals[i];  // 返回第 i 个元素的引用
}

// 要调用上面定义函数的主函数
int main ()
{

  cout << "改变前的值" << endl;
  for ( int i = 0; i < 5; i++ )
  {
    cout << "vals[" << i << "] = ";
    cout << vals[i] << endl;
  }

  setValues(1) = 20.23; // 改变第 2 个元素
  setValues(3) = 70.8; // 改变第 4 个元素

  cout << "改变后的值" << endl;
  for ( int i = 0; i < 5; i++ )
  {
    cout << "vals[" << i << "] = ";
    cout << vals[i] << endl;
  }
  return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

改变前的值
vals[0] = 10.1
vals[1] = 12.6
vals[2] = 33.1
vals[3] = 24.1
vals[4] = 50
改变后的值
vals[0] = 10.1
vals[1] = 20.23
vals[2] = 33.1
vals[3] = 70.8
vals[4] = 50

当返回一个引用时,要注意被引用的对象不能超出作用域。所以返回一个对局部变量的引用是不合法的,但是,可以返回一个对静态变量的引用。

int& func() {
  int q;
  //! return q; // 在编译时发生错误
  static int x;
  return x;   // 安全,x 在函数作用域外依然是有效的
}

以上就是详解C++ 引用的详细内容,更多关于C++ 引用的资料请关注我们其它相关文章!

(0)

相关推荐

  • C++ lambda 捕获模式与右值引用的使用

    lambda 表达式和右值引用是 C++11 的两个非常有用的特性. lambda 表达式实际上会由编译器创建一个 std::function 对象,以值的方式捕获的变量则会由编译器复制一份,在 std::function 对象中创建一个对应的类型相同的 const 成员变量,如下面的这段代码: int main(){ std::string str = "test"; printf("String address %p in main, str %s\n", &a

  • C++11右值引用和转发型引用教程详解

    右值引用 为了解决移动语义及完美转发问题,C++11标准引入了右值引用(rvalue reference)这一重要的新概念.右值引用采用T&&这一语法形式,比传统的引用T&(如今被称作左值引用 lvalue reference)多一个&. 如果把经由T&&这一语法形式所产生的引用类型都叫做右值引用,那么这种广义的右值引用又可分为以下三种类型: 无名右值引用 具名右值引用 转发型引用 无名右值引用和具名右值引用的引入主要是为了解决移动语义问题. 转发型引用的引

  • 详解C++中指针和引用的区别

    1.指针和引用的本质(是什么) (1)指针是存放内存地址的一种变量,特殊的地方就在它存放的是内存地址.因此,指针的大小不会像其他变量一样变化,只跟当前平台相关--不同平台内存地址的范围是不一样的,32位平台下,内存最大为4GB,因此只需要32bit就可以存下,所以sizeof(pointer)的大小是4字节.64位平台下,32位就不够用了,要想内存地址能够都一一表示,就需要64bit(但是目前应该没有这么大的内存吧?),因此sizeof(pointer)是8. (2)引用的本质是"变量的别名&q

  • C++中的循环引用

    虽然C++11引入了智能指针的,但是开发人员在与内存的斗争问题上并没有解放,如果我门实用不当仍然有内存泄漏问题,其中智能指针的循环引用缺陷是最大的问题. // // main.cpp // test // // Created by 杜国超 on 17/9/9. // Copyright © 2017年 杜国超. All rights reserved. // #include <iostream> #include <memory> #include <vector>

  • C++11 模板参数的“右值引用”是转发引用吗

    在C++11中,&&不再只有逻辑与的含义,还可能是右值引用: void f(int&& i); 但也不尽然,&&还可能是转发引用: template<typename T> void g(T&& obj); "转发引用"(forwarding reference)旧称"通用引用"(universal reference),它的"通用"之处在于你可以拿一个左值绑定给转发引用

  • C/C++ 数组和指针及引用的区别

    C/C++ 数组和指针及引用的区别 1.数组和指针的区别 (1)定义 数组是一个符号,不是变量,因而没有自己对应的存储空间.但是,指针是一个变量,里面存储的内容是另外一个变量的地址,因为是变量所以指针有自己的内存空间,只不过里面存储的内容比较特殊. (2)区别 a.对于声明和定义,指针和数组是不相同的,定义为数组,则声明也应该是数组,不可混淆 b.当作下标操作符时,指针和数组是等价的.a[i]会被编译器翻译成*(a+i). c.当数组声明被用作函数形参的时候,数组实际会被当作指针来使用. (3)

  • C++中引用传递与指针传递的区别(面试常见)

    最近Garena面试的过程中,面试官提了一个问题,C++中引用传递和指针传递的区别? 根据自己的经验,联想到了swap函数,只知道既可以用引用来实现,又可以用指针传递来实现,至于二者有何区别,自己还真没有考虑过. 痛定思痛,受虐之后,赶紧弥补自己的知识漏洞. 通过在网上搜集资料,自己也整理了一下. 精简版: 指针:变量,独立,可变,可空,替身,无类型检查: 引用:别名,依赖,不变,非空,本体,有类型检查: 完整版: 1. 概念 指针从本质上讲是一个变量,变量的值是另一个变量的地址,指针在逻辑上是

  • C++ 中引用与指针的区别实例详解

    C++ 中引用与指针的区别实例详解 引用是从C++才引入的,在C中不存在.为了搞清楚引用的概念,得先搞明白变量的定义及引用与变量的区别,变量的要素一共有两个:名称与空间. 引用不是变量,它仅仅是变量的别名,没有自己独立的空间,它只符合变量的"名称"这个要素,而"空间"这个要素并不满足.换句话说,引用需要与它所引用的变量共享同一个内存空间,对引用所做的改变实际上是对所引用的变量做出修改.并且引用在定义的时候就必须被初始化.     参数传递的类型及相关要点: 1 按值

  • 详解C++ 引用

    引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字.一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量. C++ 引用 vs 指针 引用很容易与指针混淆,它们之间有三个主要的不同: 不存在空引用.引用必须连接到一块合法的内存. 一旦引用被初始化为一个对象,就不能被指向到另一个对象.指针可以在任何时候指向到另一个对象. 引用必须在创建时被初始化.指针可以在任何时间被初始化. C++ 中创建引用 试想变量名称是变量附属在内存位置中的标签,您可以把引用当成是变量附属在内存位

  • 详解C++引用变量时那些你不知道的东西

    引用变量延迟绑定 我们知道引用变量定义时要立刻赋值,告诉编译器他是谁的引用.如果不赋值,编译会失败. 如果引用变量是单个定义的,对他赋值还比较简单. struct test_T { int data; //...其他成员 test_T(int _data = 0) :data(_data){} }; struct SaveTest { test_T & ref; //...其他成员 SaveTest(test_T & _ref) :ref(_ref){} }; int main(void)

  • Android 详解Studio引用Library与导入jar

    在Android项目开发过程中,Android Studio是一款非常强大的开发工具.到底有多强大,用了你就知道了. 本文我将介绍Studio引用Library开源项目与导入jar方法. 一.引用Library 1:选择你的项目,点击导航栏File -->New -->Import Module... 根据提示Import你的Library即可 此时展开你的项目即可发现你导入的Library(我导入的是IOS_Dialog_Library) 到这工作还未结束. 2:再次选择你的项目,点击Fil

  • 详解webpack引用jquery(第三方模块)的三种办法

    前言 在使用webpack作为构建工具,开发 vue项目的时候,难免会用到 jquery这种第三方插件(毕竟都是从用jquery过来的),那么怎么引用呢?接下来我来说三种方法. 1 html 模板文件引用法,这种方法最直接也是我们最熟悉,直接在项目中的网页模板文件中加入jquery的引用即可 a.引用 b.使用 2 expose-loader 引用法 a. 安装jquery npm i jquery -D b. main.js中引用 jquery import Vue from 'vue' im

  • C++ 中引用和指针的关系实例详解

    C++ 中引用和指针的关系实例详解 1.引用在定义时必须初始化,指针没有要求 int &rNum; //未初始化不能通过编译 int *pNum; //可以 2. 一旦一个引用被初始化为指向一个对象,就不能再指向 其他对象,而指针可以在任何时候指向任何一个同类型对象 int iNum = 10; int iNum2 = 20; int &rNum = iNum; &rNum = iNum2; //不能通过 3. 没有NULL引用,但有NULL指针. int *pNum = NULL

  • 详解Android studio如何导入jar包方法

    下面我就总结一下Android studio大家在导入jar包时遇到的一些问题和解决方法: 1,首先先说一下怎么在AS 中找到sdk,jdk,ndk的安装路径,可能一部分人一开始找不到,下面贴出方法: Android studio 中更改sdk的路径,如下图,在右边红色方框中更改sdk的路径 还有一种更好的方式可以把sdk,jdk,ndk的路径全部找到,首先File---Other Settings---Default Project Structure...,打开如下图界面,从红方框处即可直接

  • 详解JVM之运行时常量池

    class文件中的常量池 之前我们在讲class文件的结构时,提到了每个class文件都有一个常量池,常量池中存了些什么东西呢? 字符串常量,类和接口名字,字段名,和其他一些在class中引用的常量. 运行时常量池 但是只有class文件中的常量池肯定是不够的,因为我们需要在JVM中运行起来. 这时候就需要一个运行时常量池,为JVM的运行服务. 运行时常量池和class文件的常量池是一一对应的,它就是class文件的常量池来构建的. 运行时常量池中有两种类型,分别是symbolic refere

  • 通过npm引用的vue组件使用详解

    什么是组件:组件是Vue.js最强大的功能之一.组件可以扩展HTML元素,封装可重用的代码.在较高层面上,组件是自定义的元素,Vue.js的编译器为它添加特殊功能.在有些情况下,组件也可以是原生HTML元素的形式,以is特性扩展. 如何注册组件? 需要使用Vue.extend方法创建一个组件,然后使用Vue.component方法注册组件.Vue.extend方法格式如下: var MyComponent = Vue.extend({ // 选项...后面再介绍 }) 如果想要其他地方使用这个创

  • IOS 避免self循环引用的方法的实例详解

    IOS 避免self循环引用的方法的实例详解 示例代码: // - weak & strong #define myWeakify(VAR) \ try {} @finally {} \ __weak __typeof__(VAR) VAR##_myWeak_ = (VAR) #define myStrongify(VAR) \ try {} @finally {} \ __strong __typeof__(VAR) VAR = VAR##_myWeak_ #define myStrongif

随机推荐