对C++中引用的补充说明(实例)

#include <iostream>   
#include <string>   
using namespace std; 
 
void main(int argc,char* argv[])

    int a=10; 
    int b=20; 

    int &rn=a; 
    cout<<rn<<"|"<<a<<endl; 

    cout<<&rn<<"|"<<&a<<endl;//c++中是无法取得应用的内存地址的,取引用的地址就是取目标的地址! 
    rn=b;//把引用指向另一个目标----变量b 
    cout<<&rn<<"|"<<&a<<"|"<<&b<<endl; 

    rn=100;//试图改变b的值 
    cout<<a<<"|"<<b<<endl;//输出修改后的结果  
    cin.get(); 

}

  由于引用本身就是目标的一个别名,引用本身的地址是一个没有意义的值,所以在c++中是无法取得引用的内存地址的。取引用的地址就是取目标的地址,c++本身就根本不提供获取引用内存地址的方法。

  引用一单初始化,就不在能够被指向其它的目标,虽然编译不会出错,但操作是不起作用的,实际上还是指向最先指向的目标。

  上面代码中的rn=b实际在计算机看来就是a=b,所以修改的还是a的值。

#include <iostream>   

#include <string>   
using namespace std; 
 
void main(int argc,char* argv[])     


    int a=10; 
    void &rn=a;// 错误的,void即无类型的类型 

    int a[100]; 
    int &ra[100]=a;//错误,不能声明引用数组 

    cin.get(); 
}

  上面的两错误要记住引用的特性,void修饰是不能够声明引用的,引用是不能够声明数组的,即不能够声明引用数组。

  下面我们要说一下,也是补充中最重要最需要掌握的内容,也是对传统函数操作的内存状态的一个补充学习。

  下面我们来看一个例子:

#include <iostream>   

#include <string>   
using namespace std; 
 
float c; 
float test(float,float); 
void main(int argc,char* argv[])     


    float pn=test(3.0f,1.2f); 
    cout<<pn; 
    cin.get(); 

 
float test(float a,float b) 

    c=a*b; 
    return c; 
}

  在上面的代码中我们可能我们可能以为函数返回的就是c变量,呵呵。这么想可能就错了,普通情况下我们在函数内进行普通值返回的时候在内存栈空间内其实是自动产生了一个临时变量temp,它是返回值的一个副本一个copy,函数在return的时候其实是return的这个临时产生的副本。

  数据在内存中的情况如下图:

  上图明确表示了副本领事变量的情况。

  下面我们再来看一种情况,就是把返回值赋给引用:

#include <iostream>   

#include <string>   
using namespace std; 
 
float c; 
float test(float,float); 
void main(int argc,char* argv[])     


    float &pn=test(3.0f,1.2f);//警告:返回的将是临时变量,pn引用将成为临时变量的别名! 
    cout<<pn; 
    cin.get(); 

 
float test(float a,float b) 

    c=a*b; 
    return c; 
}

  float
&pn=test(3.0f,1.2f);这句在bc中能够编译通过,因为bc扩展设置为临时变量设置引用,那么临时变量的生命周期将和引用的生命周期一致,但在vc中却不能通过编译,因为一但test()执行过后临时变量消失在栈空间内,这时候pn将成为一个没有明确目标的引用,严重的时候会导致内存出错。

  它在内存中的情况见下图:

  我们在图中看到,由于函数仍然是普通方法返回,所以仍然会有一个副本临时变量产生,只不过,这一次只是返回一个目标地址,在main中目标地址被赋予了引用pn。

  下面我们再看一种情况,这是返回引用给变量的情况:

#include <iostream>   

#include <string>   
using namespace std; 
 
float c; 
float& test(float,float); 
void main(int argc,char* argv[])     


    float pn=test(3.0f,1.2f); 
    cout<<pn; 
    cin.get(); 

 
float &test(float a,float b) 

    c=a*b; 
    return c; 
}

  这种返回引用给变量的情况下,在内存中,test()所在的栈空间内并没有产生临时变量,而是直接将全局变量c的值给了变量pn,这种方式是我们最为推荐的操作方式,因为不产生临时变量直接赋值的方式可以节省内存空间提高效率,程序的可读性也是比较好的。

  它在内存中的情况见下图:

  最后的一种情况是函数返回引用,并且发值赋给一个引用的情况:

#include <iostream>   

#include <string>   
using namespace std; 
 
float c; 
float& test(float,float); 
void main(int argc,char* argv[])     


    float &pn=test(3.0f,1.2f); 
    cout<<pn; 
    cin.get(); 

 
float &test(float a,float b) 

    c=a*b; 
    return c; 
}

  这种情况同样也不产生临时变量,可读和性能都很好,但有一点容易弄错,就是当c是非main的局部变量或者是在堆内存中临时开辟后来又被fee掉了以后的区域,这种情况和返回的指针是局部指针的后果一样严重,会导致引用指向了一个不明确的地址,这种情况在内存中情况见下图:

  由于这种情况存在作用域的问题,故我们推荐采用第三种方式处理。

  接下来我们说几个利用引用作为左值参与计算的例子,这一点一非常重要,对于理解返回引用的函数是非常有帮助的。

#include <iostream>   

#include <string>   
using namespace std; 
 
float c; 
float& test(float,float); 
void main(int argc,char* argv[])     


    float &pn=test(3.0f,1.2f); 
    cout<<pn<<endl; 

    test(3.0f,1.2f)=12.1;//把函数作左值进行计算! 

    cout<<pn; 
    cin.get(); 

 
float &test(float a,float b) 

    c=a*b; 
    return c; 
}

  通常来说函数是不能作为左值,因为引用可以做为左值,所以返回引用的函数自然也就可以作为左值来计算了。

  在上面的代码中:

float &pn=test(3.0f,1.2f);

  进行到这里的时候pn已经指向到了目标c的地址了。

  接下来运行了

test(3.0f,1.2f)=12.1;

  把函数作左值进行计算,这里由于test是返回引用的函数,其实返回值返回的地址就是c的地址,自然c的值就被修改成了12.1。

 
 

《挑战30天C++入门极限》 对C++中引用的补充说明(实例)的更多相关文章

  1. 《挑战30天C++入门极限》C++中利用构造函数与无名对象简化运算符重载函数

        C++中利用构造函数与无名对象简化运算符重载函数 在完整描述思想之前,我们先看一下如下的例子,这个例子中的加运算符重载是以非成员函数的方式出现的: //程序作者:管宁  //站点:www.cn ...

  2. 《挑战30天C++入门极限》c++中指针学习的两个绝好例子

        c/c++中指针学习的两个绝好例子 对于众多人提出的c/c++中指针难学的问题做个总结: 指针学习不好关键是概念不清造成的,说的简单点就是书没有认真看,指针的学习犹如人在学习饶口令不多看多学多 ...

  3. 《挑战30天C++入门极限》新手入门:C/C++中的结构体

        新手入门:C/C++中的结构体 什么是结构体? 简单的来说,结构体就是一个可以包含不同数据类型的一个结构,它是一种可以自己定义的数据类型,它的特点和数组主要有两点不同,首先结构体可以在一个结构 ...

  4. 《挑战30天C++入门极限》新手入门:关于C++中的内联函数(inline)

        新手入门:关于C++中的内联函数(inline) 在c++中,为了解决一些频繁调用的小函数大量消耗栈空间或者是叫栈内存的问题,特别的引入了inline修饰符,表示为内联函数. 可能说到这里,很 ...

  5. 《挑战30天C++入门极限》新手入门:C/C++中枚举类型(enum)

        新手入门:C/C++中枚举类型(enum) 如果一个变量你需要几种可能存在的值,那么就可以被定义成为枚举类型.之所以叫枚举就是说将变量或者叫对象可能存在的情况也可以说是可能的值一一例举出来. ...

  6. 《挑战30天C++入门极限》新手入门:C/C++中数组和指针类型的关系

        新手入门:C/C++中数组和指针类型的关系 对于数组和多维数组的内容这里就不再讨论了,前面的教程有过说明,这里主要讲述的数组和指针类型的关系,通过对他们之间关系的了解可以更加深入的掌握数组和指 ...

  7. 《挑战30天C++入门极限》入门教程:实例详解C++友元

        入门教程:实例详解C++友元 在说明什么是友元之前,我们先说明一下为什么需要友元与友元的缺点: 通常对于普通函数来说,要访问类的保护成员是不可能的,如果想这么做那么必须把类的成员都生命成为pu ...

  8. 《挑战30天C++入门极限》C++面向对象编程入门:构造函数与析构函数

        C++面向对象编程入门:构造函数与析构函数 请注意,这一节内容是c++的重点,要特别注意! 我们先说一下什么是构造函数. 上一个教程我们简单说了关于类的一些基本内容,对于类对象成员的初始化我们 ...

  9. 《挑战30天C++入门极限》C++类静态数据成员与类静态成员函数

        C++类静态数据成员与类静态成员函数 在没有讲述本章内容之前如果我们想要在一个范围内共享某一个数据,那么我们会设立全局对象,但面向对象的程序是由对象构成的,我们如何才能在类范围内共享数据呢? ...

  10. 《挑战30天C++入门极限》C++类对象的复制-拷贝构造函数

        C++类对象的复制-拷贝构造函数 在学习这一章内容前我们已经学习过了类的构造函数和析构函数的相关知识,对于普通类型的对象来说,他们之间的复制是很简单的,例如: int a = 10; int ...

随机推荐

  1. 【转】SpringBoot处理url中的参数的注解

    1.介绍几种如何处理url中的参数的注解 @PathVaribale  获取url中的数据 @RequestParam  获取请求参数的值 @GetMapping  组合注解,是 @RequestMa ...

  2. ①将SVN迁移到GitLab-单分支迁移

    将SVN上的代码迁移到GitLab上,实际原理是将所迁移的服务器上,拷贝SVN上的相关代码,在服务器上生成Git相关仓库,然后推送到GitLab仓库,并保存SVN相关的提交记录,分支,标签等信息. 一 ...

  3. stack + positioned

    stack 下套container, 发现最大的显示,小的都没显示, 把所有都套个POSITIONED, 都正常显示了.

  4. 【转载】C#中使用Average方法对List集合中相应元素求平均值

    在C#的List集合操作中,有时候需要对List集合元素进行汇总求平均值,如数值类型的List集合元素,有时候对象类型的List集合也需要对集合中的元素的某个对象进行汇总求平均值,此时都可以使用到Av ...

  5. SIM800A 建立网络

    SIM800A是一款两频GSM/GPRS模块,为SMT封装.其性能稳定,外观小巧,性价比高 可以低功耗实现语音.SMS和数据信息的传输 数据传输 GPRS class 12:最大85.6 kbps(下 ...

  6. OC与swift混编 #import "项目名-Swift.h"失效问题

    由于项目多个环境部署,每次改配置比较麻烦,所以线上环境一个TARGETS,内部环境一个TARGETS, 都知道oc和swift混编的时候,会生成一个'项目名-Swift.h'文件,这个文件是隐式的,需 ...

  7. Redis除了做缓存--Redis做消息队列/Redis做分布式锁/Redis做接口限流

    1.用Redis实现消息队列 用命令lpush入队,rpop出队 Long size = jedis.lpush("QueueName", message);//返回存放的数据条数 ...

  8. Sqlite常用命令及基本知识

      sqlite菜鸟教程:http://www.runoob.com/sqlite/sqlite-tutorial.html   常用命令: .sqlite3 --从dos命令模式进入sqlite命令 ...

  9. 【zookeeper】apache-zookeeper-3.5.5的安装测试

    下载:apache-zookeeper-3.5.5.tar.gz 进入zookeeper安装目录查看lib文件夹和zookeeper-3.5.5.jar是否存在,如果没有需要手动添加,不然启动时会报错 ...

  10. 【spark】spark应用(分布式估算圆周率+基于Spark MLlib的贷款风险预测)

    注:本章不涉及spark和scala原理的探讨,详情见其他随笔 一.分布式估算圆周率 计算原理:假设正方形的面积S等于x²,而正方形的内切圆的面积C等于Pi×(x/2)²,因此圆面积与正方形面积之比C ...