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

const T

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

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

const T*

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

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

const int* 与int* const的区别

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

 int nValue = ;
int* const p = &nValue;
int *const p2 = &nValue;
//const int* 指针指向的对象不可以改变,但指针本身的值可以改变;
//int* const 指针本身的值不可改变,但其指向的对象可以改变。
int nValue1 = ;
int nValue2 = ;
int* const constPoint = &nValue1;
//constPoint = & nValue2; //错误,不能改变constPoint本身的值
*constPoint = ; //正确,可以改变constPoint所指向的对象,此时nValue1 = 40 const int nConstValue1 = ;
const int nConstValue2 = ;
const int* pPoint = &nConstValue1;
//*pPoint = 55; //错误,不能改变pPoint所指向对象的值
pPoint = &nConstValue2; //正确,可以改变pPoint指针本身的值,
//此时pPoint邦定的是nConstValue2对象,即pPoint为nConstValue2的地址

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

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

const T&

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

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

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

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

const T*&与T *const&

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

 const int nConstValue = ;                      //常量对象
const int nConstValue2 = ; //常量对象
const int* pConstValue = &nConstValue; //指向常量对象的指针
const int* pConstValue2 = &nConstValue2; //指向常量对象的指针
const int*& rpConstValue = pConstValue; //指向常量对象的指针的引用
//*rpConstValue = 10; //错误,rpConstValue指向的是常量对象,常量对象的值不可改变
rpConstValue = pConstValue2; //正确,此时pConstValue的值等于pConstValue2
//指向常量对象的指针本身是对象,引用可以改变邦定对象的值 int nValue = ;
int nValue2 = ;
int *const constPoint = &nValue; //常量指针
int *const constPoint2 = &nValue2; //常量指针
int *const &rpConstPoint = constPoint; //对常量指针的引用,邦定constPoint
//rpConstPoint = constPoint2; //错误,constPoint是常量指针,指针本身的值不可改变
*rpConstPoint = ; //正确,指针指向的对象可以改变

在函数中的应用

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

 typedef struct __Data
{
int value;
public:
__Data()
:value(){}
}Data;

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

方式<1>

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

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

方式<2>

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

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

方式<3>

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

调用如下:

 Data d();
const Data* pData = &d;
Dealwith(pData);
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; };

 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;
};

调用如下:

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

方式<2>

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

 const Data& GetData()
{
return m_data;
}

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

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

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

 Data GetData()
{
return m_data;
}

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

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

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

方式<3>

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

 void DoSomething(const Data*& pData)
{
if (pData != NULL)
{
// doto: 这里实现你要处理的功能
}
pData = &m_data;
}

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

[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. H264子宏块的划分有哪些?

    每个分割或子宏块都有一个独立的运动补偿.每个 MV 必须被编码.传输,分割的选择也需编 码到压缩比特流中.对大的分割尺寸而言,MV 选择和分割类型只需少量的比特,但运动补偿残差 在多细节区域能量将非常 ...

  2. QGrapicsItem类

    这个类翻译了好久,实在是成员函数太多了,分享出来,希望对大家有用,多多支持哦~~ 详细介绍 QGraphicsItem类是视图框架的一部分,是在一个QGraphicsScene中最基本的图形类,它为绘 ...

  3. Lua基础---lua字符串库函数详解,实例及正则表达式

    参考这个网友写的,比较简单容易理解. http://blog.csdn.net/wlgy123/article/details/49206183

  4. 9.proc目录下的文件和目录详解

    1./proc目录下的文件和目录详解 /proc:虚拟目录.是内存的映射,内核和进程的虚拟文件系统目录,每个进程会生成1个pid,而每个进程都有1个目录. /proc/Version:内核版本 /pr ...

  5. [转载] FFMPEG之AVRational TimeBase成员理解

    FFMPEG的很多结构中有AVRational time_base;这样的一个成员,它是AVRational结构的 typedef struct AVRational{    int num; /// ...

  6. 判定客户端IP所在的省市[2]

    在上一篇地址控件的基础上,添加了判定客户端IP所在省市的功能. 调用新浪提供的IP库接口:http://counter.sina.com.cn/ip/,(可直接点击链接查看返回的数据) 126提供的接 ...

  7. 查找对应jar的maven包

    当原有项目换成maven项目时,往往不知道具体jar包在maven里叫什么.这边文章教你如何去找到自己想要的jar的maven包. 工具/原料   浏览器 方法/步骤   1 登录一下网站 http: ...

  8. jenkins部署应用

    1.    系统介绍 Jenkins系统提供了一键部署的作用,整个过程有从提测的分支抓取代码,编译,打包,把打的包部署在应用服务器上,基本有Service,Web和Worker等. 2.    Jen ...

  9. Python学习流程

    这是我在过去几家公司招聘到工程师,Python入职培训的过程. 时间分为4周,全部自学,仅提供大纲.适用于Web方向: 1.Week1:读完<简明Python教程>,适应Python开发环 ...

  10. Linux IO 监控与深入分析

    https://jaminzhang.github.io/os/Linux-IO-Monitoring-and-Deep-Analysis/ Linux IO 监控与深入分析 引言 接昨天电话面试,面 ...