CC++中const关键字详解Word下载.docx
- 文档编号:20438899
- 上传时间:2023-01-23
- 格式:DOCX
- 页数:20
- 大小:25.81KB
CC++中const关键字详解Word下载.docx
《CC++中const关键字详解Word下载.docx》由会员分享,可在线阅读,更多相关《CC++中const关键字详解Word下载.docx(20页珍藏版)》请在冰豆网上搜索。
const限制了常量的使用方式,并没有描述常量应该如何分配。
如果编译器知道了某const的所有使用,它甚至可以不为该const分配空间。
最简单的常见情况就是常量的值在编译时已知,而且不需要分配存储。
―《C++ProgramLanguage》
用const声明的变量虽然增加了分配空间,但是可以保证类型安全。
C标准中,const定义的常量是全局的,C++中视声明位置而定。
用法2:
指针和常量
使用指针时涉及到两个对象:
该指针本身和被它所指的对象。
将一个指针的声明用const“预先固定”将使那个对象而不是使这个指针成为常量。
要将指针本身而不是被指对象声明为常量,必须使用声明运算符*const。
所以出现在*之前的const是作为基础类型的一部分:
char*constcp;
//到char的const指针
charconst*pc1;
//到constchar的指针
constchar*pc2;
//到constchar的指针(后两个声明是等同的)
从右向左读的记忆方式:
cpisaconstpointertochar.故pc不能指向别的字符串,但可以修改其指向的字符串的内容
pc2isapointertoconstchar.故*pc2的内容不可以改变,但pc2可以指向别的字符串
且注意:
允许把非const对象的地址赋给指向const对象的指针,不允许把一个const对象的地址赋给一个普通的、非const对象的指针。
用法3:
const修饰函数传入参数
将函数传入参数声明为const,以指明使用这种参数仅仅是为了效率的原因,而不是想让调用函数能够修改对象的值。
同理,将指针参数声明为const,函数将不修改由这个参数所指的对象。
通常修饰指针参数和引用参数:
voidFun(constA*in);
//修饰指针型传入参数
voidFun(constA&
in);
//修饰引用型传入参数
用法4:
修饰函数返回值
可以阻止用户修改返回值。
返回值也要相应的付给一个常量或常指针。
用法5:
const修饰成员函数(c++特性)
const对象只能访问const成员函数,而非const对象可以访问任意的成员函数,包括const成员函数;
const对象的成员是不能修改的,而通过指针维护的对象确实可以修改的;
const成员函数不可以修改对象的数据,不管对象是否具有const性质。
编译时以是否修改成员数据为依据进行检查。
具体展开来讲:
(一).常量与指针
常量与指针放在一起很容易让人迷糊。
对于常量指针和指针常量也不是所有的学习C/C++的人都能说清除。
例如:
constint*m1=newint(10);
int*constm2=newint(20);
在上面的两个表达式中,最容易让人迷惑的是const到底是修饰指针还是指针指向的内存区域?
其实,只要知道:
const只对它左边的东西起作用,唯一的例外就是const本身就是最左边的修饰符,那么它才会对右边的东西起作用。
美容根据这个规则来判断,m1应该是常量指针(即,不能通过m1来修改它所指向的内容。
);
而m2应该是指针常量(即,不能让m2指向其他的内存模块)。
由此可见:
1.对于常量指针,不能通过该指针来改变所指的内容。
即,下面的操作是错误的:
inti=10;
constint*pi=&
i;
*pi=100;
因为你在试图通过pi改变它所指向的内容。
但是,并不是说该内存块中的内容不能被修改。
我们仍然可以通过其他方式去修改其中的值。
//1:
通过i直接修改。
i=100;
//2:
使用另外一个指针来修改。
int*p=(int*)pi;
*p=100;
实际上,在将程序载入内存的时候,会有专门的一块内存区域来存放常量。
但是,上面的i本身不是常量,是存放在栈或者堆中的。
我们仍然可以修改它的值。
而pi不能修改指向的值应该说是编译器的一个限制。
2.根据上面const的规则,constint*m1=newint(10);
我们也可写作:
intconst*m1=newint(10);
这是,理由就不须作过多说明了。
3.在函数参数中指针常量时表示不允许将该指针指向其他内容。
voidfunc_02(int*constp)
{
int*pi=newint(100);
//错误!
P是指针常量。
不能对它赋值。
p=pi;
}
intmain()
int*p=newint(10);
func_02(p);
deletep;
return0;
4.在函数参数中使用常量指针时表示在函数中不能改变指针所指向的内容。
voidfunc(constint*pi)
不能通过pi去改变pi所指向的内容!
func(p);
}我们可以使用这样的方法来防止函数调用者改变参数的值。
但是,这样的限制是有限的,作为参数调用者,我们也不要试图去改变参数中的值。
因此,下面的操作是在语法上是正确的,但是可能破还参数的值:
#include<
iostream>
string>
//这里相当于重新构建了一个指针,韩恩贞指向相同的内存区域。
当然就可以通过该指针修改内存中的值了。
int*pp=(int*)pi;
*pp=100;
usingnamespacestd;
cout<
<
\"
*p=\"
<
*p<
endl;
(二):
常量与引用
常量与引用的关系稍微简单一点。
因为引用就是另一个变量的别名,它本身就是一个常量。
也就是说不能再让一个引用成为另外一个变量的别名,那么他们只剩下代表的内存区域是否可变。
即:
//正确:
表示不能通过该引用去修改对应的内存的内容。
constint&
ri=i;
//错误!
不能这样写。
int&
constrci=i;
由此可见,如果我们不希望函数的调用者改变参数的值。
最可靠的方法应该是使用引用。
下面的操作会存在编译错误:
voidfunc(constint&
i)
不能通过i去改变它所代表的内存区域。
func(i);
这里已经明白了常量与指针以及常量与引用的关系。
但是,有必要深入的说明以下。
在系统加载程序的时候,系统会将内存分为4个区域:
堆区栈区全局区(静态)和代码区。
从这里可以看出,对于常量来说,系统没有划定专门的区域来保护其中的数据不能被更改。
也就是说,使用常量的方式对数据进行保护是通过编译器作语法限制来实现的。
我们仍然可以绕过编译器的限制去修改被定义为“常量”的内存区域。
看下面的代码:
constinti=10;
//这里i已经被定义为常量,但是我们仍然可以通过另外的方式去修改它的值。
//这说明把i定义为常量,实际上是防止通过i去修改所代表的内存。
int*pi=(int*)&
(三):
常量函数
常量函数是C++对常量的一个扩展,它很好的确保了C++中类的封装性。
在C++中,为了防止类的数据成员被非法访问,将类的成员函数分成了两类,一类是常量成员函数(也被称为观察着);
另一类是非常量成员函数(也被成为变异者)。
在一个函数的签名后面加上关键字const后该函数就成了常量函数。
对于常量函数,最关键的不同是编译器不允许其修改类的数据成员。
classTest
public:
voidfunc()const;
private:
intintValue;
};
voidTest:
func()const
intValue=100;
上面的代码中,常量函数func函数内试图去改变数据成员intValue的值,因此将在编译的时候引发异常。
当然,对于非常量的成员函数,我们可以根据需要读取或修改数据成员的值。
但是,这要依赖调用函数的对象是否是常量。
通常,如果我们把一个类定义为常量,我们的本意是希望他的状态(数据成员)不会被改变。
那么,如果一个常量的对象调用它的非常量函数会产生什么后果呢?
classFred{
voidinspect()const;
voidmutate();
};
voidUserCode(Fred&
changeable,constFred&
unChangeable){changeable.inspect();
//正确,非常量对象可以调用常量函数。
changeable.mutate();
//正确,非常量对象也允许修改调用非常量成员函数修改数据成员。
unChangeable.inspect();
//正确,常量对象只能调用常理函数。
因为不希望修改对象状态。
unChangeable.mutate();
//错误!
常量对象的状态不能被修改,而非常量函数存在修改对象状态的可能
从上面的代码可以看出,由于常量对象的状态不允许被修改,因此,通过常量对象调用非常量函数时将会产生语法错误。
实际上,我们知道每个成员函数都有一个隐含的指向对象本身的this指针。
而常量函数则包含一个this的常量指针。
如下:
voidinspect(constFred*this)const;
voidmutate(Fred*this);
也就是说对于常量函数,我们不能通过this指针去修改对象对应的内存块。
但是,在上面我们已经知道,这仅仅是编译器的限制,我们仍然可以绕过编译器的限制,去改变对象的状态。
voidFred:
inspect()const
Atthebeginning.intValue=\"
intValue<
//这里,我们根据this指针重新定义了一个指向同一块内存地址的指针。
//通过这个新定义的指针,我们仍然可以修改对象的状态。
Fred*pFred=(Fred*)this;
pFred->
intValue=50;
Fred:
inspect()called.intValue=\"
Fredfred;
fred.inspect();
上面的代码说明,只要我们愿意,我们还是可以通过常量函数修改对象的状态。
同理,对于常量对象,我们也可以构造另外一个指向同一块内存的指针去修改它的状态。
这里就不作过多描述了。
另外,也有这样的情况,虽然我们可以绕过编译器的错误去修改类的数据成员。
但是C++也允许我们在数据成员的定义前面加上mutable,以允许该成员可以在常量函数中被修改。
mutableintintValue;
但是,并不是所有的编译器都支持mutable关键字。
这个时候我们上面的歪门邪道就有用了。
关于常量函数,还有一个问题是重载。
voidfunc();
constfunctioniscalled.\"
func()
non-constfunctioniscalled.\"
voidUserCode(Fred&
fred,constFred&
cFred)
{cout<
fredisnon-constobject,andtheresultoffred.func()is:
\"
fred.func();
cout<
cFredisconstobject,andtheresultofcFred.func()is:
cFred.func();
}intmain(){Fredfred;
UserCode(fred,fred);
return0;
}输出结果为:
fredisnon-constobject,andtheresultoffred.func()is:
non-constfunctioniscalled.
cFredisconstobject,andtheresultofcFred.func()is:
constfunctioniscalled.
从上面的输出结果,我们可以看出。
当存在同名同参数和返回值的常量函数和非常量函数时,具体调用哪个函数是根据调用对象是常量对像还是非常量对象来决定的。
常量对象调用常量成员;
非常量对象调用非常量的成员。
总之,我们需要明白常量函数是为了最大程度的保证对象的安全。
通过使用常量函数,我们可以只允许必要的操作去改变对象的状态,从而防止误操作对对象状态的破坏。
但是,就像上面看见的一样,这样的保护其实是有限的。
关键还是在于我们开发人员要严格的遵守使用规则。
另外需要注意的是常量对象不允许调用非常量的函数。
这样的规定虽然很武断,但如果我们都根据原则去编写或使用类的话这样的规定也就完全可以理解了。
(四):
常量返回值
很多时候,我们的函数中会返回一个地址或者引用。
调用这得到这个返回的地址或者引用后就可以修改所指向或者代表的对象。
这个时候如果我们不希望这个函数的调用这修改这个返回的内容,就应该返回一个常量。
这应该很好理解,大家可以去试试。
+++++++++++++++++++++++++++++++++++++++
c++中const
1.const常量,如constintmax=100;
优点:
const常量有数据类型,而宏常量没有数据类型。
编译器可以对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查,并且在字符替换时可能会产生意料不到的错误(边际效应)
2.const修饰类的数据成员。
classA
constintsize;
…
const数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的。
因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。
所以不能在类声明中初始化const数据成员,因为类的对象未被创建时,编译器不知道const数据成员的值是什么。
如
constintsize=100;
//错误
intarray;
//错误,未知的size
const数据成员的初始化只能在类的构造函数的初始化表中进行。
要想建立在整个类中都恒定的常量,应该用类中的枚举常量来实现。
{…
enum{size1=100,size2=200};
intarray1[size1];
intarray2[size2];
枚举常量不会占用对象的存储空间,他们在编译时被全部求值。
但是枚举常量的隐含数据类型是整数,其最大值有限,且不能表示浮点数。
3.const修饰指针的情况,见下式:
intb=500;
constint*a=&
[1]
intconst*a=&
[2]
int*consta=&
[3]
constint*consta=&
[4]
如果你能区分出上述四种情况,那么,恭喜你,你已经迈出了可喜的一步。
不知道,也没关系,我们可以参考《Effectivec++》Item21上的做法,如果const位于星号的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;
如果const位于星号的右侧,const就是修饰指针本身,即指针本身是常量。
因此,[1]和[2]的情况相同,都是指针所指向的内容为常量(const放在变量声明符的位置无关),这种情况下不允许对内容进行更改操作,如不能*a=3;
[3]为指针本身是常量,而指针所指向的内容不是常量,这种情况下不能对指针本身进行更改操作,如a++是错误的;
[4]为指针本身和指向的内容均为常量。
4.const的初始化
先看一下const变量初始化的情况
1)非指针const常量初始化的情况:
Ab;
constAa=b;
2)指针const常量初始化的情况:
A*d=newA();
constA*c=d;
或者:
constA*c=newA();
3)引用const常量初始化的情况:
Af;
constA&
e=f;
//这样作e只能访问声明为const的函数,而不能访问一般的成员函数;
[思考1]:
以下的这种赋值方法正确吗?
constA*c=newA();
A*e=c;
[思考2]:
A*constc=newA();
A*b=c;
5.另外const的一些强大的功能在于它在函数声明中的应用。
在一个函数声明中,const可以修饰函数的返回值,或某个参数;
对于成员函数,还可以修饰是整个函数。
有如下几种情况,以下会逐渐的说明用法:
A&
operator=(constA&
a);
voidfun0(constA*a);
voidfun1()const;
//fun1()为类成员函数
constAfun2();
1)修饰参数的const,如voidfun0(constA*a);
v
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- CC const 关键字 详解