这里的T指的是一种数据类型,可以是int、long、doule等基本数据类型,也可以是自己类型的类型class。单独的一个const你肯定知道指的是一个常量,但const与其他类型联合起来的众多变化,你是不是就糊涂了?下面我们一一来解析。

const T

定义一个常量,声明的同时必须进行初始化。一旦声明,这个值将不能被改变。

  1. int i = ;
  2. const int constInt = ; //正确
  3. const int constInt2 = i; //正确
  4. constInt = ; //错误,常量值不可被改变
  5. const int constInt3; //错误,未被初始化

const T*

指向常量的指针,不能用于改变其所指向的对象的值。

  1. const int i = ;
  2. const int i2 = ;
  3. const int* pInt = &i; //正确,指向一个const int对象,即i的地址
  4. //*pInt = 10; //错误,不能改变其所指缶的对象
  5. pInt = &i2; //正确,可以改变pInt指针本身的值,此时pInt指向的是i2的地址
  6. const int* p2 = new int(); //正确,指向一个new出来的对象的地址
  7. delete p2; //正确
  8. //int* pInt = &i; //错误,i是const int类型,类型不匹配,不能将const int * 初始化为int *
  9. int nValue = ;
  10. const int * pConstInt = &nValue;//正确,可以把int *赋给const int *,但是pConstInt不能改变其所指向对象的值,即nValue
  11. *pConstInt = ; //错误,不能改变其所指向对象的值

const int* 与int* const的区别

指针本身就是一种对象,把指针定义为常量就是常量指针,也就是int* const的类型,也可以写成int *const,声明时必须初始化。

  1. int nValue = ;
  2. int* const p = &nValue;
  3. int *const p2 = &nValue;
  4. //const int* 指针指向的对象不可以改变,但指针本身的值可以改变;
  5. //int* const 指针本身的值不可改变,但其指向的对象可以改变。
  6. int nValue1 = ;
  7. int nValue2 = ;
  8. int* const constPoint = &nValue1;
  9. //constPoint = & nValue2; //错误,不能改变constPoint本身的值
  10. *constPoint = ; //正确,可以改变constPoint所指向的对象,此时nValue1 = 40
  11.  
  12. const int nConstValue1 = ;
  13. const int nConstValue2 = ;
  14. const int* pPoint = &nConstValue1;
  15. //*pPoint = 55; //错误,不能改变pPoint所指向对象的值
  16. pPoint = &nConstValue2; //正确,可以改变pPoint指针本身的值,
  17. //此时pPoint邦定的是nConstValue2对象,即pPoint为nConstValue2的地址

const int* const 是一个指向常量对象的常量指针,即不可以改变指针本身的值,也不可以改变指针指向的对象。

  1. const int nConstValue1 = ;
  2. const int nConstValue2 = ;
  3. const int* const pPoint = &nConstValue1;
  4. //*pPoint = 55; //错误,不能改变pPoint所指向对象的值
  5. //pPoint = &nConstValue2; //错误,不能改变pPoint本身的值

const T&

对常量(const)的引用,又称为常量引用,常量引用不能修改其邦定的对象。

  1. int i = ;
  2. const int constInt = ;
  3. const int& rConstInt = constInt; //正确,引用及邦定的值都是常量
  4. rConstInt = ; //错误,不能改变引用所指向的对象

允许为一个常量引用邦定一个非常量对象、字面值,甚至是表达式;引用的类型与引用所指向的类型必须一致。

  1. int i = ;
  2. int& rInt = i; //正确,int的引用
  3. const int constInt = ;
  4. const int& rConstInt = constInt; //正确,引用及邦定的值都是常量
  5. const int& rConstInt2 = rInt; //正确,用rInt邦定的对象进行赋值
  6. rInt = ; //这时,rConstInt2、rInt、i的值都为30
  7. //rConstInt2 = 30; //错误,rConstInt2是常量引用,rConstInt2本身不能改变所指向的对象
  8.  
  9. int i2 = ;
  10. const int& rConstInt3 = i2; //正确,用非常量的对象为其赋值
  11. const int& rConstInt4 = i + i2; //正确,用表达式为其赋值,值为45
  12. i = ; //此时i=20, rInt = 20, rConstInt4 = 45,说明rConstInt4邦定的是i + i2的临时变量
  13. const int& rConstInt5 = ; //正解,用一个常量值为其赋值

const T*&与T *const&

指向常量对象的指针的引用,这可以分两步来理解:1.const T*是指向常量的指针;2.const T*&指向常量的指针的引用。

  1. const int nConstValue = ; //常量对象
  2. const int nConstValue2 = ; //常量对象
  3. const int* pConstValue = &nConstValue; //指向常量对象的指针
  4. const int* pConstValue2 = &nConstValue2; //指向常量对象的指针
  5. const int*& rpConstValue = pConstValue; //指向常量对象的指针的引用
  6. //*rpConstValue = 10; //错误,rpConstValue指向的是常量对象,常量对象的值不可改变
  7. rpConstValue = pConstValue2; //正确,此时pConstValue的值等于pConstValue2
  8. //指向常量对象的指针本身是对象,引用可以改变邦定对象的值
  9.  
  10. int nValue = ;
  11. int nValue2 = ;
  12. int *const constPoint = &nValue; //常量指针
  13. int *const constPoint2 = &nValue2; //常量指针
  14. int *const &rpConstPoint = constPoint; //对常量指针的引用,邦定constPoint
  15. //rpConstPoint = constPoint2; //错误,constPoint是常量指针,指针本身的值不可改变
  16. *rpConstPoint = ; //正确,指针指向的对象可以改变

在函数中的应用

我们直接从需求出来,假设有这样一个数据结构:

  1. typedef struct __Data
  2. {
  3. int value;
  4. public:
  5. __Data()
  6. :value(){}
  7. }Data;

1.希望传入一个对象,又不想让函数体修改这个对象。

方式<1>

  1. void Dealwith(const Data& data)
  2. {
  3. cout << data.value << endl;
  4. //data.value = 5; //错误,data是常量引用,不能改变其邦定的对象
  5. }

这种方式还有一个好处是只有在调用函数的时候会邦定对象,传递的是对象的引用,而不是对象,减少函数调用时对象赋值的花销。

方式<2>

  1. void Dealwith(const Data* pData)
  2. {
  3. cout << pData->value << endl;
  4. //pData->value = 5; //错误,pData是指向常量对象的指针,不能改变其指向的对象
  5. }

这种方式与void Dealwith(const Data& data)的功能相同

方式<3>

  1. Data g_data();
  2. void Dealwith(const Data*& pData)
  3. {
  4. cout << pData->value << endl;
  5. //pData->value = 5; //错误,pData邦定的是指向常量对象的指针,常量对象的指针不能改变其指向的对象
  6. pData = &g_data; //正确,pData是[指向常量对象的指针]的引用,引用可改变其邦定的对象
  7. }

调用如下:

  1. Data d();
  2. const Data* pData = &d;
  3. Dealwith(pData);
  4. cout << pData->value << endl; //此时pData->value为20,d.value还是10

这种方式函数未改变传入的对象的值,但可以返回另外一个对象的指针。注意返回的指针必须指向全局的对象,如果返回函数内定义的对象,退出函数作用域后,其指针将无效,这是非常危险的;如果Dealwith是成员函数,也可以返回指向成员的指针。

2.在类中的使用,返回一个类的成员,但不希望调用方修改这个成员。

方式<1>

class MyData { public : MyData(std::string name, Data data) { m_name = name; m_data = data; } const Data* GetData() { return &m_data; } private: std::string m_name; Data m_data; };

  1. class MyData
  2. {
  3. public :
  4. MyData(std::string name, Data data)
  5. {
  6. m_name = name;
  7. m_data = data;
  8. }
  9.  
  10. const Data* GetData()
  11. {
  12. return &m_data;
  13. }
  14.  
  15. private:
  16. std::string m_name;
  17. Data m_data;
  18. };

调用如下:

  1. MyData mydata("", Data());
  2. const Data* pData = mydata.GetData();
  3. cout << pData->value << endl; //pData->value = 100
  4. //pData->value = 50; //错误,pData是指向常量对象的指向,不能改变其指向对象的值

方式<2>

有人可能会问GetData也可以写成这样:

  1. const Data& GetData()
  2. {
  3. return m_data;
  4. }

这样的话,调用方常常容易写成这样:

  1. MyData mydata("", Data());
  2. Data data = mydata.GetData(); //这会有个赋值的过程,会把mydata.m_data赋给data
  3. cout << data.value << endl; //data.value = 100
  4. data.value = ; //正确,data.value=50,但mydata.m_data.value还是100

这样调用时会有一个结果赋值的过程,如果Data是一个复杂的类,会有较大的开销,其效果与下面这种方式是一样的:

  1. Data GetData()
  2. {
  3. return m_data;
  4. }

当然,如果调用方这样使用是正确的:

  1. const Data& GetData()
  2. {
  3. return m_data;
  4. }
  1. MyData mydata("", Data());
  2. const Data& data = mydata.GetData(); //这会有个赋值的过程,会把mydata.m_data赋给data
  3. cout << data.value << endl; //data.value = 100
  4. //data.value = 50; //错误,data是一个对常量的引用,不能改变其邦定的对象

这对调用方的技术能力要求比较高,如果你是设计方,一定要尽量使接口简单易用。

方式<3>

如果你要传入一个Data进行一些处理,处理完后返回类的成员m_data,可如下实现:

  1. void DoSomething(const Data*& pData)
  2. {
  3. if (pData != NULL)
  4. {
  5. // doto: 这里实现你要处理的功能
  6. }
  7. pData = &m_data;
  8. }

如果您有什么疑惑和想法,请在评论处给予反馈,您的反馈就是最好的测评师!由于本人技术和能力有限,如果本博文有错误或不足之处,敬请谅解并给出您宝贵的建议!

[from:http://blog.csdn.net/luoweifu/article/details/45600415]

[转] const T、const T*、T *const、const T&、const T*& 的区别的更多相关文章

  1. C++中的const成员函数(函数声明后加const,或称常量成员函数)用法详解

    http://blog.csdn.net/gmstart/article/details/7046140 在C++的类定义里面,可以看到类似下面的定义: 01 class List { 02 priv ...

  2. C++ —— 非类中使用const定义常量的初始化,以及#define和typedef的区别

    总结一下在非类中使用const关键字定义常量时的初始化问题,亲测VS2015.顺便记录#define宏和typedef的区别. 1 首先对const声明的常量的初始化做简单小结: , w2 = , w ...

  3. 常量指针(const X*)和指针常量(X* const)

    const X* 类型的指针(指向常量的指针),此指针的地址是一个变量,是可以修改的:但其所指向的内容是常量,是不可以修改的. 例如: 1: char name[5] = "lisi&quo ...

  4. 为什么const对象只能调用const成员函数,而不能调用非const成员函数?

    在c++中,我们可以用const来定义一个const对象,const对象是不可以调用类中的非const成员函数,这是为什么呢?下面是我总结的一些原理. 假设有一个类,名字为test代码如下: clas ...

  5. CTC安装及其错误解决办法:binding.cpp:92:49: error: cannot convert ‘THCudaTensor*’ to ‘const THFloatTensor*’ for argument ‘1’ to ‘int64_t THFloatTensor_size(const THFloatTensor*, int)’

    CTC安装: 1. 在终端执行命令:git clone https://github.com/SeanNaren/warp-c) (效果如下图,大家不用管我前面括号的内容,那是我打开的虚拟环境) 2. ...

  6. 预处理、const、static与sizeof-内联函数与宏有什么区别

    1:二者的区别如下: (1)内联函数在编译时展开,宏在预编译时展开. (2)在编译的时候,内联函数可以直接被镶嵌到目标代码中,而宏只是一个简单的文本替换. (3)内联函数可以完成诸如类型检测.语句是否 ...

  7. 预处理、const、static与sizeof-static全局变量与普通的全局变量有什么区别

    1:全局变量的说明之前再加上static就构成了静态的全局变量.全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式.这两者在存储方式上并无不同.这两者的区别在于,非静态全局变量的作用域是整 ...

  8. const,static,extern 简介

    const,static,extern 简介 一.const与宏的区别: const简介:之前常用的字符串常量,一般是抽成宏,但是苹果不推荐我们抽成宏,推荐我们使用const常量. 执行时刻:宏是预编 ...

  9. C++中的const

    一,C++中const的基本知识 1.C++中const的基本概念 1.const是定义常量的关键字,表示只读,不可以修改. 2.const在定义常量的时候必须要初始化,否则报错,因为常量无法修改,只 ...

  10. const extern static 终极指南

    const extern static 终极指南 不管是从事哪种语言的开发工作,const extern static 这三个关键字的用法和原理都是我们必须明白的.本文将对此做出非常详细的讲解. co ...

随机推荐

  1. PostgreSQL窗口函数(转)

    转自:http://time-track.cn/postgresql-window-function.html PostgreSQL提供了窗口函数的特性.窗口函数也是计算一些行集合(多个行组成的集合, ...

  2. 2017年--10年java大神告诉你开发最常用的百分之二十的技术有哪些?

    首先题主说的20%我不知道从哪方面去理解.接下来我会将自己多年来工作中会经常使用到的技术列出来. 1.html.css 2.java工作原理(jvm) 3.java语法.数据结构和算法 4.java语 ...

  3. 后台导入导出Excel

    Excel导出 定义数据模型 参考财付通的批量提现Excel的格式,定义模型如下 private int recordId; //提现id private String cname; //提现人名称 ...

  4. 微软原版WINDOWS10-LTSB-X64位操作系统的全新安装与优化

    原版WINDOWS10_LTSB_X64位操作系统,安装U盘的制作 1.在一台能正常运行的电脑上,下载原版WINDOWS10_LTSB_X64位操作系统镜像(ISO)文件: 2.运行UltraISO. ...

  5. numpy 矩阵相关函数

    我们 知道,矩阵在python里面用的不少,所以记载下关于矩阵的操作 numpy.zeros():可以用来构造全零矩阵 >>> zeros(3) array([ 0.,  0.,   ...

  6. hexo搭建个人主页托管于github

    之前学习了 如何利用Github免费搭建个人主页,今天利用hexo来快速生成个人网页托管于github上. hexo系列教程:(一)hexo介绍 什么是hexo hexo是一个基于Node.js的静态 ...

  7. COGS 2638. 数列操作ψ 线段树

    传送门 : COGS 2638. 数列操作ψ 线段树 这道题让我们维护区间最大值,以及维护区间and,or一个数 我们考虑用线段树进行维护,这时候我们就要用到吉司机线段树啦 QAQ 由于发现若干次an ...

  8. JS ready和onload事件 比较分析

    页面加载完成有两种事件: 一是ready,表示文档结构已经加载完成(不包含图片等非文字媒体文件); 二是onload,指示页 面包含图片等文件在内的所有元素都加载完成.(可以说:ready 在onlo ...

  9. bzoj 3295 (洛谷3157、3193) [Cqoi2011]动态逆序对——树套树 / CDQ分治

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3295 题目--洛谷3157:https://www.luogu.org/problemnew ...

  10. 常用map总结

    Map map = new HashMap(); Iterator it = map.entrySet().iterator(); while (it.hasNext()) { Map.Entry e ...