C++:关键字const (转)

常对象,常数据成员,常成员函数,指向对象的常指针,指向常对象的指针,对象的常引用。
author: ZJ 07-12-11
如果需要保证数据不被修改,则可以使用const,即把有关的数据定义为常量。
1.常对象
在定义对象时使用关键字const指定对象为常对象
 
1.1常对象的形式定义
类名 const 对象名(实参表); const 类名 对象名(实参表);
class Time{
 public:
    Time(int=0,int=0,int=0);
    void call();
    int hour;
    int min;
    int sec;
};  
 
Time::Time(int h,int m,int s){
 hour=h;
 min=m;
 sec=s;
}
 
Time const t(2,3,4);//t为常对象
1.2使用常对象的规则
[1]常对象中的数据成员为常变量且必须要有初始值;
 
[2]在所有的场合中,常对象中的所有数据成员的值都不能被修改。
 
[3]如果一个对象被声明为常对象,则不能调用该对象的非const型的成员函数(除了由系统自动调用的隐式的构造函数和析构函数)。
这是为了防止这些函数会修改常对象中的数据成员的值。编译系统会检查函数的声明,只要发现调用了常对象的成员函数,且该函数未被声明为const就报错。编译器不会进入函数去检查它的代码,看它是否修改了常对象中的数据成员的值,这是因为函数的定义与函数的声明可能不在同一个源程序文件中。而编译则是以一个源程序文件为单位的,这样就无法发现两个源程序文件之间是否有矛盾。如果有错,只有在连接或运行阶段才能发现。
注意,常对象中是允许非const型的成员函数存在的,只不过你无法调用它们。
 
[4]如果需要访问常对象中的成员函数,则需要将其声明为const。表示这是一个常成员函数,常成员函数可以访问常对象中的数据成员,但仍然不允许修改常对象中数据成员的值。
class Time{
 public:    
   
    void call() const;
};  
 
void Time::call() const{
 cout<<"Call:"<<hour<<"-"<<min<<"-"<<sec<<endl;
}
 
 Time const t(2,3,4);
 t.call();
[5]如果需要修改常对象中某个数据成员的值,则可将该数据成员声明为mutable。
class Time{
 public:    
    void setHour(int) const;
mutable int hour;//可以修改hour的值,通过setHour()
int min; //不可以修改min的值,read-only
int sec; //不可以修改sec的值,read-only
};
 
void Time::setHour(int h) const{
 hour=h;
}
 Time const t(2,3,4);
 t.setHour(5);
2.常对象成员
可以在声明类时将成员声明为const,即声明常数据成员常成员函数
 
2.1常数据成员
用关键字const来声明常数据成员。在类体中声明了某一个常数据成员后,该类所有对象中的该数据成员的值都是不能改变的,但不同对象中该数据成员的值是可以不同的(在定义对象时给定)。
只能通过构造函数的参数初始化表对常数据成员进行初始化。
class Time{
 public:
    Time(int,int,int);
    const int hour;//声明hour为常数据成员
    int min;
    int sec;
};  
 
Time::Time(int h,int m,int s):hour(h){//通过参数初始化表hour(h)对常数据成员hour初始化
 min=m;
 sec=s;
}
 
 Time t(2,3,4);
注意,不能采用在构造函数中对常数据成员赋初值的方法。
const int hour;
int min;
int sec;
Time::Time(int h,int m,int s){ error:uninitialized member 'Time::hour' with 'const' type 'const int'
 hour=h;
 min=m;
 sec=s;
}
 
2.2常成员函数
常成员函数只能引用本类中的数据成员而不能修改它们。
class Time{
 public:    
   
void call() const; //声明常成员函数
};  
 
void Time::call() const{//定义常成员函数
 cout<<"Call:"<<hour<<"-"<<min<<"-"<<sec<<endl;
}
const是函数类型的一部分,在声明函数和定义函数时都要有const关键字,在调用时不必加const。常成员函数可以访问const数据成员,也可以访问非const数据成员。常成员函数不能调用另一个非const成员函数。
下表给出了const与非const数据成员及const与非const成员函数之间的访问关系。其中,get指访问;set指修改它的值。
数据成员
非const成员函数
const成员函数
非const数据成员
get/set
get/NO set
const数据成员
get/NO set
get/NO set
const对象的数据成员
NO get/NO set
get/NO set
3.指向对象的常指针
将指向对象的指针变量声明为const,并使之初始化,这样指针值始终保持为其初始值,不能改变,即指向始终不变。
Time t(2,3,4);
Time *const p=&t;//声明常指针p并使其指向对象t
定义指向对象的常指针的一般形式为,
类名 *const 指针变量名=对象地址;
必须在定义指针时,就确定它指向的对象,下面的写法错误,
Time t(2,3,4);
Time *const p;error:uninitialized const 'p'
p=&t;
在初始化常指针后,不能使其指向另一个对象,下面写法错误,
Time t(2,3,4);
Time *const p=&t;
Time tt(1,1,1);
p=&tt;error:assignment of read-only variable 'p'
指向对象的常指针的值是不能改变的,即始终指向同一个对象,但可以改变其所指向的对象中数据成员的值(前提是该数据成员是public)。
class Time{
 public:
    void call();
    int hour;
 private:
    int min;
 
void Time::call() {
 cout<<"Call:"<<hour<<"-"<<min<<"-"<<sec<<endl;
}
int main(){
 Time t(2,3,4);
 Time *const p=&t;
 p->call();
 p->hour=0;
 //p->min=0; error:`int Time::min' is private
 p->call();
结果:
Call:2-3-4
Call:0-3-4
如果想将一个指针变量固定地与一个对象相联系(即该指针变量始终指向一个对象),可以将它指定为const型指针变量。往往用常指针作为函数的形参,目的是不允许在函数执行过程中改变指针变量的值,使其始终指向原来的对象。如果在函数执行过程中修改了形参的值,编译器就会抱错。
4.指向常对象的指针变量
4.1指向常变量的指针变量
下面定义了一个指向常变量的指针变量ptr,它指向的char变量是常变量,不能通过ptr改变它的值。
const char *ptr;
定义指向常变量的指针变量的一般形式为,
    const 类型名 * 指针变量名;
[1]如果一个变量已被声明为常变量,只能用指向常变量的指针变量指向它,而不能用一般的(指向非const型变量的)指针变量去指向它。(因为主导权在于指针,由指针是否为指向const型来决定是否可以改变所指向变量的值)
const char c[]="china";     
const char *p1; //指向常变量的指针变量
p1=c;//ok
char * p2;//一般的指针变量
p2=c;error: invalid conversion from 'const char*' to 'char*'
[2]指向常变量的指针变量除了指向常变量外,还可以指向非const的变量。此时不能通过此指针变量改变该变量的值。但还是可以通过指针访问该变量的值。(因为主导权在于指针,由指针是否为指向const型来决定是否可以改变所指向变量的值)
char c[]="china";
const char *ptr; 
ptr=c;//ok
*ptr='C';//将首字母改为大写error:assignment of read-only location
但可以通过变量自己改变(不通过指针的途径)
c[0]='C';
[3]如果函数的形参是指向非const型变量的指针,实参只能用指向非const变量的指针,而不能用指向const变量的指针(“实参”和“形参”的映射关系中“形参”起主导地位)。这样,在执行函数的过程中可以改变形参指针变量所指向的变量的值(“改变指针变量所指向的变量的值”的限制权在“形参”)。
int main(){
 void fun(int *); //函数声明
 int num[]={1,2,3,4};  
 int *p1;//定义一个一般的指针变量
 const int *p2;//定义一个指向整型常变量的指针变量
 p1=num;
 p2=num;   
 fun(p1);//ok
 fun(p2); error:invalid conversion from 'const int*' to 'int*'
}
 
void fun(int *p){//定义一个函数,它的形参是一般的指针
     *p='0'; //改变所指向的变量的值
}
[4]如果函数的形参是指向const型变量的指针,在执行函数过程中显然不能改变指针变量所指向的变量的值(“改变指针变量所指向的变量的值”的限制权在“形参”),因此允许实参既可以是指向const变量的指针,也可以是指向非const变量的指针(“实参”和“形参”的映射关系中“形参”起主导地位)。
void fun(const int *p){//定义一个函数,它的形参是指向const变量的指针
     *p='0'; error:assignment of read-only location
}
在main函数中使用该函数,
void fun(const int *); //函数声明
int num[]={1,2,3,4};
int *p1;//定义一个一般的指针变量
const int *p2;//定义一个指向整型常变量的指针变量
p1=num;
p2=num;
fun(p1);//ok
fun(p2); //ok
下表给出了用指针变量作形参时形参和实参的对应关系。
形参
实参
合法否
改变指针变量所指向的变量的值
指向非const型变量的指针
非const变量的地址
合法
可以
指向非const型变量的指针
const变量的地址
非法
/
指向const型变量的指针
非const变量的地址
合法
不可以
指向const型变量的指针
const变量的地址
合法
不可以
“改变指针变量所指向的变量的值”的限制权在“形参”;“实参”和“形参”的映射关系中“形参”起主导地位;const的类型的指针可以指向非const类型的变量,但非const类型的指针则不能指向const类型的变量,因为主导权在于指针,由指针是否为指向const型来决定是否可以改变所指向变量的值。
 
4.2指向常对象的指针变量
也是4条原则,和4.1是一一对应的。
[1]如果一个对象已被声明为常对象,只能用指向常对象的指针变量指向它,而不能用一般的(指向非const型对象的)指针变量去指向它。(因为主导权在于指针,由指针是否为指向const型来决定是否可以改变所指向对象中成员的值)
Time const t(2,3,4);
const Time *p1;//定义一个指向常对象的指针变量
Time *p2;// 定义一个一般的指针变量
p1=&t;//ok
p2=&t;error:invalid conversion from `const Time*' to `Time*'
[2]指向常对象的指针变量除了指向常对象外,还可以指向非const的对象。此时不能通过此指针变量改变该对象中成员的值。但还是可以通过指针访问该对象中的成员。(因为主导权在于指针,由指针是否为指向const型来决定是否可以改变所指向对象中成员的值)
Time t(2,3,4);
const Time *p=&t;
cout<<p->hour<<endl;//ok
p->hour=0;error:assignment of data-member 'Time::hour' in read-only structure
[3]指向常对象的指针常用于函数的形参,目的是保护形参指针所指向的对象,使它在函数执行过程中不被改变。
void fun(const Time *p){//定义一个函数,它的形参是指向const对象的指针
     p->hour=11;error: assignment of data-member 'Time::hour' in read-only structure
     cout<<p->hour<<endl;
}
 
int main(){
 void fun(const Time *); //形参是指向常对象的指针变量
 Time t(2,3,4);
 fun(&t);
前面已经说过,“改变指针变量所指向的变量的值”的限制权在“形参”;因此对于fun函数来说,它的形参是一个指向常变量的指针,这样可以有效地保证在fun函数内部,不会通过指针来篡改传递进来的对象的数据;
 
[4]“实参”和“形参”的映射关系中“形参”起主导地位;所以这里不必将对象t定义为常对象,即Time const t(2,3,4);也能将其作为实参传入;
反之则不行,如果形参指定为指向一般的对象的指针,而实参为一个常对象的入口地址,则出错(“实参”和“形参”的映射关系中“形参”起主导地位)。下面代码错误,
void fun(Time *p){
     p->hour=11;//ok
     cout<<p->hour<<endl;
}
 
int main(){
 void fun(Time *); 
 Time const t(2,3,4);
 fun(&t); error:invalid conversion from 'const Time*' to 'Time*'
...
5.常指针与指向常变量/常对象指针的区别
常指针表示指针变量中存放的值(它是一个地址)是不可改变的(一旦初始化后);指向常变量/常对象指针表示指针所指向的数据(通过地址找到)是不可改变的。
6.对象的常引用
下面的例子是将对象的引用作为函数的形参,
class Time{
 public:    
    Time(int,int,int);       
int hour;
};  
 
void fun(Time &t){
   t.hour=11;
}
 
int main(){
 void fun(Time &); 
 Time t(2,3,4);
 fun(t);
如果不希望对象t的值被修改,如上面fun函数的操作,则可以将fun的形参改为常引用。
void fun(const Time &t){
 t.hour=11; error:assignment of data-member 'Time::hour' in read-only structure  
}
 
int main(){
 void fun(const Time &); 
 Time t(2,3,4);
 fun(t);
7.对象的常引用与指向对象的常指针及指向常对象的指针的区别
个人认为,首先使用对象的常引用的目的等同于使用指向常对象的指针的目的,即不让函数去修改该对象中的数据。但同时由于对象的引用本身是某个对象的别名,自声明起它就必须初始化且再也不能作为其他对象的引用了,所以它又具备了指向对象的常指针的效果。
const int ci=1;//常变量
const int *const ci_p=&ci;//指向常量的指针常量
const int &ci_r=ci;//常引用

本文出自 “子 孑” 博客,请务必保留此出处http://zhangjunhd.blog.51cto.com/113473/54783
本文出自 51CTO.COM技术博客
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章