C++中const与指针、引用的分析

发布者:daits摸鱼的最新更新时间:2015-05-05 来源: 51hei关键字:C++  const  指针  引用 手机看文章 扫描二维码
随时随地手机看文章
C++中函数的参数相比C语言中的函数参数要复杂的多,其中主要的原因是C++中引入了引用以及const限定符。这两个对象的引入,使得C++中的函数参数变得异常的复杂多变,每一种类型都具有比较适合的使用范围。

本文详细的分析const与指针、引用在一起存在情况下的情况分析。
 
首先介绍引用
    引用是对象的别名,必须在初始化的过程中与一个具体的对象绑定起来,绑定完成以后就再也不能够修改了,引用貌似和指针有很大的相似性,但是引用是引用,是一个别名,而指针是一个变量,只是变量中保存的是对象的地址,引用并不分配新的内存空间。因此一个引用与一块内存区域绑定,是这块内存区域的别名,就如同数组名一样,数组是内存中的一块区域,数组名是这块区域的名字,但是在内存中并不存在额外的区域保存数组名。引用就是一块内存区域的别名。C++中变量实质上就是一块内存区域,我们在用&i就可以得到变量i的地址,也就是说变量是这块区域的名字。对变量的操作实质上就是对这块内存中内容的操作。别名是这块区域的另一个名字。采用任何一个名字对这块区域修改都会影响区域的内容。

 

    int vari = 10;
    int &refr = vari;
    vari = 20;
    cout << refr << " " << vari << endl;
    refr = 30;
    cout << refr << " " << vari << endl;

上面的int &refr = vari实质上就是变量vari的别名,也就是保存vari变量地址的别名,这个地址中保存的是一个int类型的数据,数据可以通过vari来操作,可以修改。因为refr是一个别名,也可以通过该别名对这块内存区域进行操作。也就是说别名的操作就是针对一块特定的内存区域,这个通过变量操作的效果是一致的。
其次说说const限定符
    const的引入使得在函数操作中的一些问题复杂了,但是更加的合理了,const限定符是指上是指一个常量。这个常量在一般的情况下就是限定变量不能修改,但是当这个限定符与引用、指针结合在一起时,使得很多的问题不在明朗。
 
    1、const与指针

 

    int *ptr;
    const int *ciptr;
    int const *icptr;
    int * const cptr;
    const int * const cicptr;

上面是关于const与指针结合时的各种情况,这并不只是C++中经常遇到的问题,在C语言中也会有类似的讨论,虽然const并不是C语言中的关键字。
int * ptr         是指定义一个指向int 类型的指针ptr。
const int *ciptr 是指定义一个指向const int 类型的指针ciptr,这是const 限定的是(* ciptr),也就是对指针解引用,即const限定的就是数据本身,而非指针。所以ciptr是一个指向常int型数据的指针。
int const * icptr其实和上面的const int *ciptr是一致的因为const只是一个限定符,在int前还是后都 没有影响,他限定的仍然是(*icptr),并不是icptr,也就是icptr也是指向常int型数据的指针,也就是说在icptr这个指针看来,它指向的数据是常数,是不能改变的,但是是否真的不能改变,需要依据实际的类型的分析,只是不能通过这个指针来改变。也就是说该指针是一个自以为自己指向常量的指针。
int * const cptr 这时候我们还是结合const的位置分析,const限定的是cptr,cptr我们可以知道是一个指针,也就是说const限定的是一个指针,而不是指针指向的数据,也就是说这种定义实质上是定义一个常指针,也就是指针指向的地址是不变的,也就是cptr是不能被赋值的,不能采用这个指针指向其他的对象或者地址。但是这个地址中的数据并不是常数,是可以改变的,可以通过对*cptr进行修改。这种指针实质上也就使得指针的效果大大减小,并不常用。
const int * const cicptr 这种定义结合上面的分析我们知道是一个指向常量的常指针,也就说这个指针指向的地址是一个常地址,指针不能指向其他的对象或者地址。同时对指针cicptr来说,我指向的这个地址中的内容也是不能修改的,是一个常量,是不能修改的,但是该地址的数据能否修改还需要进行实际的分析。
 
    2、关于指针的初始化、赋值的问题
    对于const类型的数据,如果需要定义指针,这时候只能定义const类型的数据,这是为什么呢?因为对于const对象来说,数据是肯定不能修改的,如果定义指向非const的指针,程序员可能就会通过指针来修改对象的值,但是const对象是不能被修改的,肯定会出现错误。因此const的变量只能采用指向常量的指针来指向。一般来说如果将一个非const指针指向了一个const对象,编译的过程中就会抛出如下的错误:

    invalid conversion from ‘const int*’ to ‘int*’

    但是对于指向常量的指针并不一定指向的数据就是常量,这是一个非常重要的技术点,指向常量的指针指向的数据只是针对这个指针而言,他认为自己指向的数据是常量,休想通过他来修改指向的对象。也就是说指向常量的指针在初始化、赋值的时可以初始化或者赋值为非const变量的地址,即可以指向非const的对象,只是不能通过该指针来修改对象罢了。
    同时需要注意:对于指向const的指针,初始化过程比较方便,不要求是const对象的地址,可以采用非const对象的地址初始化,甚至还可以采用指向非const的指针直接赋值初始化,这时指针自己认为自己指向的对象是常量。但是不能将指向const的指针直接赋值给指向非const的指针,如果不小心赋值也会出现上面出现的问题。下面参看一段小的代码,说明其中的一些问题。

 

    #include
    #include
    #include

    using namespace std;

    int main()
    {
            int num = 20;
            const int array_size = 10;

        
            int *pnum = #
            const int * cpnum = #
            /*const int *指针可以采用int *指针直接初始化*/
            const int *csize1 = pnum;


            /*但是int * 指针不能采用const int *制作初始化*/
            //int *psize = &array_size;
            /*const类型数据只能采用指向const的指针来指向*/
            const int *csize = &array_size;

            cout << "Before change..." << endl;
            cout << "The num of num = " << num << endl;
            cout << "*pnum = " << *pnum << " "
                    << "*cpnum = " << *cpnum << " "
                    << "csize1 = " << *csize1 << endl;

            num = 30;
        
            cout << "After changed..." << endl;
            cout << "The num of num = " << num << endl;
            cout << "*pnum = " << *pnum << " "
                    << "*cpnum = " << *cpnum << " "
                    << "csize1 = " << *csize1 << endl;

            return 0;
    }

 
    从上面的结果我们可以知道指向const的指针可以采用非const变量的地址进行初始化或者赋值操作,同时也可以采用指向非const指针直接初始化指向const的指针。同时指向const的指针不能赋值给指向非const的指针,会出现const int *不能转换到int *的问题。

    3、const与引用
    关于const和引用在一起情况下也存在一些类似于指针的情况,但是毕竟引用相比指针要简单,这时候情况也比较简单.但是我认为分析引用应该与分析指针是一样也存在类似的问题。但是引用就只有两种,非const的引用和const的引用。[page]

 

    int num = 10;

    int &newname = num;
    const int &othername = num;

   引用主要是上面的两种,这两种的区别相对来说比较大,而且加入了const限定符以后,引用的能力往往变的更加的强大。
   一般来说对于const对象而言,一般只能采用const引用,这与前面的const对象只能采用指向const对象的原因是一样的,如果对引用没有进行限定,可能会通过引用修改数据,这是不允许的。也就是说const引用与指向const对象的指针有一定的相似性,即不能通过这个引用或者指针来修改原来的数据。保证数据的const特性。也就是说非const引用不能引用const对象,如果不小心引用编译器会出现下面的错误:

    invalid initialization of reference of type ‘int&’ from expression of type ‘const int’

因此非const引用只能针对非const的同类型数据。这是需要注意的。比如string,和字符串字面值都不能直接引用。因为类型不相同,这是在C++函数定义中经常出现的问题,在后期的博文中再分析。在引用中加入const的就是对于这个引用而言,不能通过自己来修改原始的数据,这与指向const的指针有很大的相似性,
 
    但是往往const引用的初始化并不一定要去对象是const的,甚至可以是不同类型的对象,这种优越性是连指针(指针只能指向同一类型的数据,如果一定要指向需要强制类型转换)都没有的,也就是可以将不同类型的非const或者const对象来初始化一个const引用。但是这个const限定符就限定了该引用指向的对象是不能通过该引用来修改的。如果尝试采用const的引用进行修改,编译器会出现如下的错误:

    error: assignment of read-only reference...

    综合上面的描述可知:非const引用只能绑定到该引用同类型(string和字符串字面值(const char *)之间都不可以)的非const对象,而const引用则可以绑定到任意的一种对象上(非const、const、甚至不同类型),这种差别在函数的参数中有较大的体现。
    通过下面的例子来说明一下上面的分析:

 

    #include
    #include
    #include

    using namespace std;

    int main()
    {
            int num = 20;
            const int array_size = 10;

            int &pnum = num;
            const int &cpnum = num;
            /*采用引用直接初始化const类型的引用*/
            const int &csize1 = pnum;

            /*const的变量不能采用非const的引用*/
            //int &psize = array_size;
            /*const类型数据只能采用指向const的指针来指向*/
            const int &csize = array_size;

            cout << "Before change..." << endl;
            cout << "The num of num = " << num << endl;
            cout << "pnum = " << pnum << " "
                    << "cpnum = " << cpnum << " "
                    << "csize1 = " << csize1 << endl;

            num = 30;
            cout << "After the first changed..." << endl;
            cout << "The num of num = " << num << endl;
            cout << "pnum = " << pnum << " "
                    << "cpnum = " << cpnum << " "
                    << "csize1 = " << csize1 << endl;
       
            /*通过引用修改变量的值*/
            pnum = 40;
            cout << "After the second changed..." << endl;
            cout << "The num of num = " << num << endl;
            cout << "pnum = " << pnum << " "
                    << "cpnum = " << cpnum << " "
                    << "csize1 = " << csize1 << endl;

            /*不能采用const的引用修改对象,
            *这与指向const的指针特性的相似处*/
            /*
            csize1 = 50;
            cout << "After the second changed..." << endl;
            cout << "The num of num = " << num << endl;
            cout << "pnum = " << pnum << " "
                    << "cpnum = " << cpnum << " "
                    << "csize1 = " << csize1 << endl;
            */

            double dnum = 10.1;
            /*非const的引用只能绑定相同类型的对象*/
            //int &dname = dnum;

            /******************************************
            *const引用可以绑定不同类型的对象,
            *因此const引用就能更加方便的作为函数的形参
            *******************************************/
            const int &dothername = dnum;

            return 0;
    }


上面的实验结果基本上符合分析的结论。
 
总结
    const的使得引用与指针的变化更加复杂,总体而言,const主要是保证了通过指针或者引用不修改原始的数据,但是至于原始的数据是否可以修改,这就需要参看数据的类型。
    在存在const的对象中,只能采用包含限定符const的引用或者指向const的指针来操作。
    const的引用比较强大,初始化的过程中可以采用任意的对象,const对象,非const对象,甚至其他类型的数据。const引用支持隐式类型转换。而指向const的指针则不能,只能指向同一类型的数据,但是可以采用强制类型转换,初始化或者赋值过程中对数据类型没有要求,可以是const对象的地址,也可以是非const对象的地址。
    const引用和指向const对象的指针都是自己以为自己指向的对象是不能修改的,采用const的指针或者引用就能避免原始数据修改。

关键字:C++  const  指针  引用 引用地址:C++中const与指针、引用的分析

上一篇:C++标准库中的list设计
下一篇:赫纳法则

推荐阅读最新更新时间:2024-03-16 14:00

C8051F005在高速误码测试系统中的运用
引 言 随着通信技术的不断发展,通信系统信号处理越来越快。在这种情况下,对于高速通信系统性能的检验,就需要高速误码测试仪。目前市而上已有多种误码测试仪。国内产品的信号处理速度较低,而国外产品的功能虽然比较完善,处理速度很高,但其价格也相对较高。本文根据Vitesse公司的VSC8228芯片特点,利用C8051F005单片机设计出一种价廉的高速误码测试仪。下面将对其软硬件设计,特别是C8051F005与上位机的串口通信以及与VSC8228的SPI通信进行详细探讨。 1 误码测试系统概述 Cygnal公司的单片机C8051F005具有高速8051微控制器内核,速度可达25 MIPS,指令为流水线指令结构,70
[测试测量]
<font color='red'>C</font>8051F005在高速误码测试系统中的运用
ADS1.2使用jlink调试程序(调试芯片s3c2440 arm9)
一、软件安装 ADS1.2下载: http://down.drv5.cn/www.drv5.cn/arm ads1.2.rar jlink驱动下载: http://fastsoft.onlinedown.net/down/JLink_Windows_V630d.exe S3C2440led裸机程序(GT2440开发板的): https://download.csdn.net/download/u012577474/11249524 下载,安装上面的3个软件。 二、CodeWarrior编辑arm程序 ADS安装后,会安装以下这些工具。 这里先打开CodeWarrior,导入我们的led裸机程序。 程序目录: 导入程
[单片机]
ADS1.2使用jlink调试程序(调试芯片s3<font color='red'>c</font>2440 arm9)
DVB-C数字机顶盒解决方案
       引言              在我国,数字电视节目在许多省市已经开始试播,由于用户端使用的基本都是模拟电视机,无法接收数字信号,因此需要一种接收装置来担当二者之间的桥梁,这就是机顶盒(SetTopBox,简称STB)。它是一种扩展电视机功能的一种新的家用电器。它可以把卫星直播数字电视信号、地面数字电视信号、有线电视网数字信号甚至互联网的数字信号转换成模拟电视机可以接收的信号,使现有的模拟电视机用户也能分享数字化革命带来的科技成果。文中主要介绍笔者开发的基于DVB-C的有线数字电视机顶盒。        DVB-C数字机顶盒的硬件设计        LSI2005主芯片介绍        DVB-C数字机顶
[嵌入式]
基于AT89C51单片机16×16LED汉字点阵滚动显示的设计
LED显示屏是利用发光二极管点阵模块或像素单元组成的平面式显示屏幕。它具有发光率高、使用寿命长、组态灵活、色彩丰富以及对室内外环境适应能力强等优点。并广泛的用于公交汽车、商店、体育场馆、车站、学校、银行、高速公路等公共场所的信息发布和广告宣传。LED显示屏发展较快,本文讲述了基于AT89C51单片机16×16LED汉字点阵滚动显示的基本原理、硬件组成与设计、程序编写与调试、Proteus软件仿真等基本环节和相关技术 1 硬件电路组成及工作原理 本产品采用以AT89C51单片机为核心芯片的电路来实现,主要由AT89C51芯片、时钟电路、复位电路、列扫描驱动电路(74HCl54)、16×16LED点阵5部分组成,如图1
[单片机]
基于AT89<font color='red'>C</font>51单片机16×16LED汉字点阵滚动显示的设计
51单片机STC89C52点亮一个LED(IO口的位操作)
程序源码 /*-----------------------包含头文件区域-------------------------*/ #include reg52.h //单片机头文件 /*-----------------------端口/引脚定义区域----------------------*/ sbit LED=P2^0; //位定义P2.0引脚名为LED /*-----------------------主函数区域-----------------------------*/ void main() { LED=0; //LED端口输出低电平,即点亮LED, while(1); //死循
[单片机]
51单片机STC89<font color='red'>C</font>52点亮一个LED(IO口的位操作)
基于S3C2440的能量色散X射线荧光光谱仪
X射线荧光分析是一种快速、准确而又经济的多元素分析方法 。目前,X射线荧光分析技术已被广泛应用于地质、冶金、化工、材料、石油、医疗等领域,尤其是能量色散X射线荧光EDXRF(Energy Dispersive X-Ray Fluorescence)光谱仪,由于具有体积小、价格低廉、自动化程度高等优点,已成为普遍多元素同时分析的有力手段。 EDXRF光谱仪利用X射线荧光对于不同元素具有不同能量的特点,依靠探测器实现对测试样品中元素的定性、定量分析。随着电子学技术、计算机科学技术以及半导体材料的发展,特别是嵌入式技术的应用,为X射线荧光光谱仪智能化、小型化及高性能提供了必要的硬件基础。本文提出了基于S3C2440嵌入式处理器
[单片机]
s3c2440裸机-代码重定位(2.编程实现代码重定位)
1.引入链接脚本 我们上一节讲述了为什么要重定位代码,那么怎么去重定位代码呢? 上一节我们发现 arm-linux-ld -Ttext 0 -Tdata 0x30000000 这种方式编译出来的bin文件有800多M,这肯定是不行的,那么需要怎么把.data段重定位到sdram呢? 可以通过AT参数指定.data段在编译时的存放位置,我们发现这样指定太不方便了,而且不好确定要放在bin文件的哪个位置。这里就要引入链接脚本,它可以帮我们解决这个不必要的麻烦。 链接脚本格式 格式如下图: 我们来看一个具体的例子: SECTIONS { . = 0x00000000; //表示当前地址为0 . = AL
[单片机]
s3<font color='red'>c</font>2440裸机-代码重定位(2.编程实现代码重定位)
单片机C语言教程-运算符和表达式
  单片机C语言教程-运算符和表达式   运算符的种类、优先级和结合性   c语言中运算符和表达式数量之多,在高级语言中是少见的。正是丰富的运算符和表达式使c语言功能十分完善。这也是c语言的主要特点之一。   c语言的运算符不仅具有不同的优先级,而且还有一个特点,就是它的结合性。在表达式中,各运算量参与运算的先后顺序不仅要遵守运算符优先级别的规定,还要受运算符结合性的制约,以便确定是自左向右进行运算还是自右向左进行运算。这种结合性是其它高级语言的运算符所没有的,因此也增加了c语言的复杂性。   运算符的种类c语言的运算符可分为以下几类:   1.算术运算符   用于各类数值运算。包括加(+)、减(-)、乘(*)、除(/)、求余(或称
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved