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技術博客
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章