C/C++中const的用法 分类: C/C++ 2015-07-05 00:43 85人阅读 评论(0) 收藏
const是C语言的关键字,经C++进行扩充,变得功能强大,用法复杂。const用于定义一个常变量(只读变量),当const与指针,引用,函数等结合起来使用时,情况会变得复杂的多。下面将从五个方面总结const的用法。
1.const位置
const位置较为灵活,一般来说,除了修饰一个类的成员函数外,const不会出现先一条语句的最后。示例如下:
#include <iostream>
using namespace std;
int main(int argc,char* argv[])
{
int i=5;
const int v1=1;
int const v2=2;
const int* p1;
int const * p2;
//以下三条语句报编译错误,为什么?
//const * int p3;
//int* const p3=&v1;
//int * const p3;
int * const p3=&i;
const int* const p4=&v1;
int const * const p5=&v2;
const int & r1=v1;
int const & r2=v2;
//以下语句报编译错误,为什么?
//const & int r3;
//以下语句报警告,并忽略const
int& const r4=i;
cout<<*p4<<endl;
return 0;
}
阅读以上程序,得出如下结论:
(1)程序输出结果是1,以上程序演示const的位置与它的语义之间的关系,看似复杂,
实际有规律可循。
(2)const和数据类型结合在一起时形成所谓的“常类型”,利用常类型可申明或定义
常变量。const用来修饰类型时,既可以放在类型前面,也可以放在类型后面,如const int i和int const i是合法且等价的。用常类型申明或定义变量时,const只会出现在变量前面。
(3)const和被修饰的类型之间不能有其他标识符。
(4)int const p和int const p是不同的申明语句,原因是前者const修饰的是int,后者const修饰的是int*。前者表示指针p指向整型常变量(指针所指单元的内容不允许修改),而指针本身可以指向其他的常变量,即p为指向常量的指针——常量指针。后者表示指针p本身的值不可修改,一旦p指向某个整型变量之后就不能指向其他的变量,即p是个指针常量。
(5)引用本身可以理解为指针常量,在引用前使用const没有意义。上例中int & const r4=I;中const是多余的。即没有引用常量的说法,只有常引用。常引用指被引用对象是一个常量,不允许通过引用和修改被引用对象的值。
在很多情况下,为表达同一种语义,可将const放在不同的位置。但在某些情况下,const只能放在特定的位置,考查const配合二重指针的例子,代码如下:
int main(int argc,char* argv[])
{
//const配合二重指针
int const **p1;
int* const * p2;
int i=5;
int j=6;
const int * ptr1=&i;
int * const ptr2=&j;
p1=&ptr1;
p2=&ptr2;
cout<<**p1<< " "<<**p2<<endl;
return 0;
}
阅读以上代码得出如下结论:
(1)程序的运行结果是:5 6
(2)int const *p1和int const * p2申明的二重指针p1和p2的含义完全不同。P1不
是指针常量,它所指向的变量的类型是int const (指向整型常量的指针)。P2也不是指针常量,其指向的变量类型是int const(整型指针常量)。若按p1=&ptr1和p2=&ptr2赋值,均产生编译错误。
2.const对象和对象的const成员
const定义一个基本类型的变量是不允许修改该变量的值。const修饰类的对象称为常对象,const修饰的类成员函数称为常函数。考查如下代码:
#include <iostream>
using namespace std;
class A{
int num;
public:
A(){num =5;};
void disp();
void disp() const;
void set(int n){num=n;};
};
void A::disp(){
cout<<"Another version of disp()"<<endl;
}
void A::disp() const{
cout<<num<<endl;
}
int main(int argc,char* argv[])
{
A a1;
a1.set(3);
a1.disp();
A const a2;
a2.disp();
return 0;
}
程序执行结果是:
Another version of disp()
5
阅读以上程序,得出如下结论:
(1)常函数的申明与定义分开进行时,两边都要使用const关键字,否则发生编译错误。
(2)只有类的非静态成员函数可以被申明为常函数,原因是静态成员函数不含this指针,属于类级别的函数。其它类型的函数(如外部函数等)不能被申明为常函数。
(3)一个类的两个成员函数,如果函数的返回值类型、函数名、函数的参数列表完全相同,一个是常函数,一个是普通函数,那么它们构成重载关系。如上例中的void disp()和void disp() const。这是因为void disp()和void disp() const具有不同的签名。C++把this指针也作为参数评估的一部分,由于上面的函数被定义成class A的成员函数,那么它们最终会被看作void disp(A*)和void disp(const A*),从而构成重载。
(4)非只读对象(如a1)调用某个函数时,先寻找它的非const函数版本,如果没有找到,再调用它的const函数版本。而常对象(a2),只能调用类中定义的常函数,否则出现编译错误。
(5)存在const和非const版本的成员函数时,普通对象若想调用const函数,应该通过建立该对象的常引用或指向该对象的常指针。如上面的程序,要用对象a1调用常函数disp(),可以使用如下语句:
((const A&)a1).disp();
或者:
((const A*)&a1)->disp();
(6)非只读对象中,也可以将部分数据成员定义为常量,称为类对象的常量成员。类对象的非静态常量成员必须在构造函数中初始化,且只能借助于初始化列表,因为初始化列表才是初始化,构造函数中通过赋值运算符进行的是赋值,并非初始化。
3.const修饰函数的参数和函数的返回值
在定义函数时常用到const,主要用来修饰参数和返回值。其目的是让编译器为程序员做变量的只读性检查,以使程序更加健壮。考查如下代码:
void disp1(const int &ri){
cout<<ri<<endl;
}
void disp2(const int i){
cout<<i<<endl;
}
const int disp3(const int& ri){
cout<<ri<<endl;
return ri;
}
int& disp4(int& ri){
cout<<ri<<endl;
return ri;
}
const int& disp5(int& ri){
cout<<ri<<endl;
return ri;
}
int main(int argc,char* argv[])
{
int n=5;
disp1(n);
disp2(n);
disp3(n);
disp4(n)=6;
disp5(n);//disp5(n)=6;是错误的
getchar();
return 0;
}
程序运行结果是:
5
5
5
5
6
阅读以上代码得出如下结论:
(1)const修饰传递调用的形参申明为常量,没有实用价值。如上例中的void disp2(cons tint i)这样的申明没有意义,因为形参i的改变并不影响实参的值。
(2)函数的返回值是值类型时,被const修饰没有意义,因为此时返回值是一个非左
值,本身就不能改变,上例中const int disp3(cons tint& ri)对返回值的const限定是多余的。
(3)const修饰值类型的形参时,不构成函数重载,如void disp(const int i)与void disp(int i)。但当const修饰非值类型(引用、指针)的形参时构成函数重载,如void disp(const int& i)与void disp(int& i)。
4.常见的对const的误解
(1)误解一:用const修改的变量值一定是不能改变的。当const修饰的局部变量存储在非只读存储器中,通过指针可间接修改。
(2)误解二:常引用或常指针,只能指向常变量,这是一个极大的误解。常引用或者常指针只能说明不能通过该引用(或者该指针)去修改被引用的对象,至于被引用对象原来是什么性质是无法由常引用(常指针)决定的。
5. 将const类型转化为非const类型
使用C++中cons_cast运算符可去除复合类型中的const或volatile属性。当大量使用const_cast是不明智的,只能说程序存在设计缺陷。使用方法见下例:
void constTest(){
int i;
cout<<"please input a integer:";
cin>>i;
const int a=i;
int& r=const_cast<int&>(a);//若写成int& r=a;则发生编译错误
++r;
cout<<a<<endl;
}
int main(int argc,char* argv[])
{
constTest();
return 0;
}
程序输入5,输出6。
阅读以上程序,得出如下结论:
(1)const_cast运算符的语法形式是const_cast< type_id> (expression)。
(2)const_cast只能去除目标的const或者volatile属性,不能进行不同类型的转换。如下转换就是错误的:
cons tint A={1,2,3};
char* p=const_cast< char*>(A);//不能由const int[]转换为char*
(3)一个变量被定义为只读变量(常变量),那么它永远是常变量。cosnt_cast取消的是间接引用时的改写权限,而不能改变变量本身的const属性。
(4)利用传统的C语言中的强制类型转换也可以将const type*类型转换为type*类型,或者将const type&转换为type&类型。但是使用const_cast会更好一些,因为const_cast转换能力较弱,目的明确,不易出错,而C风格的强制类型转换能力太强,风险较大,故建议不要采用C风格的强制类型转换。
参考文献
[1]陈刚.C++高级进阶教程[M].武汉:武汉大学出版社,2008.
版权声明:本文为博主原创文章,未经博主允许不得转载。
C/C++中const的用法 分类: C/C++ 2015-07-05 00:43 85人阅读 评论(0) 收藏的更多相关文章
- AndroidManifest.xml中的application中的name属性 分类: android 学习笔记 2015-07-17 16:51 116人阅读 评论(0) 收藏
被这个不起眼的属性折磨了一天,终于解决了. 由于项目需要,要合并两个android应用,于是拷代码,拷布局文件,拷values,所有的都搞定之后程序还是频频崩溃,一直没有找到原因,学android时间 ...
- 随机带权选取文件中一行 分类: linux c/c++ 2014-06-02 00:11 344人阅读 评论(0) 收藏
本程序实现从文件中随即选取一行,每行被选中的概率与改行长度成正比. 程序用一次遍历,实现带权随机选取. 算法:假设第i行权重wi(i=1...n).读取到文件第i行时,以概率wi/(w1+w2+... ...
- JavaScript中的一些细节 分类: C1_HTML/JS/JQUERY 2014-08-05 16:45 384人阅读 评论(0) 收藏
1.设置id / class等属性 用 setAttribute 设置一些常规属性如 id ,className 的时候经常不起作用,只能用 object.id = value 这样来设置 news_ ...
- 递归查找无效的符号链接 分类: linux c/c++ 2014-06-02 00:14 345人阅读 评论(0) 收藏
本程序实现在指定目录下递归查找无效的符号链接. 1.设计思路 逐个读取给定目录中的目录项,判断类型 (1)若为目录,则读取该目录中的目录项并判断类型: (2)若为链接文件,则读取出其指向文件的名称(绝 ...
- UI基础:UIButton.UIimage 分类: iOS学习-UI 2015-07-01 21:39 85人阅读 评论(0) 收藏
UIButton是ios中用来响应用户点击事件的控件.继承自UIControl 1.创建控件 UIButton *button=[UIButton buttonWithType:UIButtonTyp ...
- 共享内存+互斥量实现linux进程间通信 分类: Linux C/C++ 2015-03-26 17:14 67人阅读 评论(0) 收藏
一.共享内存简介 共享内存是进程间通信中高效方便的方式之一.共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针,两个进程可以对一块共享 ...
- Matlab调用C程序 分类: Matlab c/c++ 2015-01-06 19:18 464人阅读 评论(0) 收藏
Matlab是矩阵语言,如果运算可以用矩阵实现,其运算速度非常快.但若运算中涉及到大量循环,Matlab的速度令人难以忍受的.当必须使用for循环且找不到对应的矩阵运算来等效时,可以将耗时长的函数用C ...
- Mahout快速入门教程 分类: B10_计算机基础 2015-03-07 16:20 508人阅读 评论(0) 收藏
Mahout 是一个很强大的数据挖掘工具,是一个分布式机器学习算法的集合,包括:被称为Taste的分布式协同过滤的实现.分类.聚类等.Mahout最大的优点就是基于hadoop实现,把很多以前运行于单 ...
- const char*, char const* and char *const 分类: C/C++ OpenCV 2014-11-08 18:10 114人阅读 评论(0) 收藏
const char*, char const*, char*const的区别问题几乎是C++面试中每次都会有的题目. 事实上这个概念谁都有只是三种声明方式非常相似很容易记混. Bjarne在他的 ...
随机推荐
- 获得APP当前显示的viewController
UIViewController* topViewController = ((UINavigationController*)self.window.rootViewController).topV ...
- 编译内核实现iptables防火墙layer7应用层过滤 (三)
在前面的两篇文章中我们主要讲解了Linux防火墙iptables的原理及配置规则,想博友们也都知道iptables防火墙是工作在网络层,针对TCP/IP数据包实施过滤和限制,属于典型的包过滤防火墙.以 ...
- 开启GZIP(转)
因为在做一个项目,项目里面服务器主要提供数据,但是数据多了文件就大了,比较浪费流量和时间,我们便用Gzip来处理.我在本机上是apache,服务器上是IIS6.0,用的是php,那么我就在这里分享一下 ...
- CSS之transition过渡练习
代码: <!DOCTYPE html><html><head> <title>transition</title> <meta cha ...
- 解决maven创建web项目卡死在generator插件(转)
如下图所示:在Properties中添加一个参数archetypeCatalog=internal,不加这个参数,在maven生成骨架的时候将会非常慢,有时候会直接卡住. 理由 archetypeCa ...
- 【转】C#多线程
C#中的多线程编程 C#是.Net平台的通用开发工具,它能够建造所有的.Net应用.在.Net中所有线程都运行在应用程序域(AppDomain)中,这也许让你想到Win32进程,实际上它们还是有很大的 ...
- RSA_SHA256数字签名
------------------ rsa 加密与解密 ----------------------------- 数论知识的实际应用: rsa加密 把问题归结为对数字的加密. 被加密的数字为明文. ...
- VIm 一些常用的设置
一些常用的vim设置 以下内容皆来源于网络,感谢原作者.如果引用出处错误,请告知以便修改. 1. vim的几种模式和按键映射 转载自:[1] Map是Vim强大的一个重要原因,可以自定义各种快捷键 ...
- LayaAir引擎——(九)
var h = new Array(); var j = new Array(); var xbCursor = 0; function xbinit() { xbinitName(); xbRect ...
- Win10/UWP开发—SystemNavigationManager
Win10系统为确保所有应用中的一致导航体验,提供后退导航功能.当你的应用在手机.平板电脑上或者在支持系统后退功能的电脑或笔记本电脑上运行时,系统会在"后退"按钮被按下时通知你的应 ...