《c++物件導向程式設計(第2版)》「3.6共用資料的保護」p90-p98
分析乙個普通例子:
1 #include
2 using namespace std;
3 4 class time
5 ;8 void show();
9 void display();
10 private:
11 int hour;
12 int minute;
13 int sec;
14 };
15 16 void time::show()
17 21
22 void time::display()
23 27
28 int main()
29 上面的例子中time::show()改變time類中hour成員變數的值;time::display()沒有修改time類中的成員變數。
(1)如果乙個物件被定義為常物件,則通過該物件只能呼叫它的常成員函式,而不能呼叫該物件的普通成員函式(除了由系統自動呼叫的隱式的建構函式與析構函式)。如:
上面**中line 30 修改為const time t1(13,34,45);
則 line 31 t1.show() //企圖呼叫常物件t1中普通成員函式,非法
line 32 t1.display()//
企圖呼叫常物件t1中普通成員函式,非法
這是為了防止普通成員函式會修改常物件中的資料成員,那麼:time::display()並沒有修改常物件中的資料成員,為什麼不允許呢?
分析:因為不能僅靠程式設計者這種細緻的檢查保證程式不出錯,編譯系統充分考慮到可能出現的情況,對不安全因素進行排斥。那麼:為什麼編譯系統不專門檢查函式的**,看它是否修改常物件中的資料成員的值呢?實際上,函式的定義與函式的宣告可能不在同乙個原始檔中。而編譯器則是以乙個原始檔為單位,無法測出兩個原始檔之間是否矛盾。如果有錯,只有在連線或執行階段才能發現。這樣就給除錯程式帶來不便。
現在,編譯系統只檢查函式的宣告,只要發現呼叫了常物件的成員函式,而且該函式未被宣告為const就會報錯。
注意:1)函式的宣告與定義中都要加const,函式呼叫不用加const
2)const新增位置:void display() const;
(2)常成員函式可以訪問常物件中的資料成員,但不允許修改常物件中的資料成員,(除非常物件中的資料成員宣告為mutable)
(3)常物件必須有初值。
常資料成員的值不能改變,只能通過建構函式的引數初始化表對常資料成員進行初始化,任何其它函式都不能對常資料成員賦值。
宣告常成員函式的一般格式為:
型別名 函式名(參數列) const
const
為函式型別的一部分,在宣告函式和定義函式時都要有關鍵字const,在呼叫時不必加const。
const資料成員可以被const成員函式引用,也可被非const的成員函式引用。
說明:「2.2中講到的const資料成員可以被非const的成員函式引用」與 1中提到「const物件不能被非const的普通成員函式引用」是否矛盾??《c++面向程式設計(第2版)》p93提到「不要誤認為常物件中的成員函式都是常成員函式。常物件只能保證其資料成員是常資料成員,其值不被改變」,這樣看來常物件是否等於把物件中的資料成員都定義為const??
分析:雖然從理解上說,定義常物件時候由此產生的常成員變數等同於直接在類體中定義常成員變數,但是在編譯器看來,這兩種方式產生的常變數不同。
(1)常物件產生的常成員變數,即便函式內定義了修改它的函式,只要該函式沒有被呼叫,編譯就能通過。如下圖:
(2)直接在類體中定義的常變數,只要定義了修改常變數的函式體,即便該函式體沒有被呼叫,編譯也無法通過。如下:
由於以上原因造成了「常物件中的資料成員不能被非const成員函式訪問」與「const資料成員可以被非const成員函式訪問」的區別。
將指標變數宣告為const型,這樣指標變數始終保持初值,不能改變,即其所指向不變。
timet1(10,12,15),t2;
time *const ptr1; //const
位置在指標變數名前面,指定ptr1是常指標變數
ptr1 =&t1; //ptr1
指向物件t1,此後不能再改變指向
ptr1 =&t2; //
錯誤,ptr1不能改變指向
注意:指向物件的常指標變數的值不能改變,即始終指向同乙個物件,但可以改變其所指向物件(如t1)的值。
什麼時候需要用指向物件的常指標呢?如果想將乙個指標變數固定位址與乙個物件相聯絡(即該指標變數始終指向乙個物件),可以將它指定為const型指標變數。這樣可以防止誤操作,增加安全性。
往往用常指標作為函式的形參,目的是不允許在函式執行過程中改變指標變數的值,使其始終指向原來的物件。
常變數的指標:
(1)如果乙個變數已被宣告為常變數,只能用指向常變數的指標變數指向它,而不能用一般的(指向非const型變數的)指標去指向它。
const char c=」boy」;//定義const型的char資料
const char *p1;//定義p1為指向const型的char變數的指標變數
p1=c; //合法,p1指向常變數(char陣列的首元素)
char *p2 = c; //不合法,p2不是指向常變數的指標變數
(2)指向常變數的指標除了可以指向常變數外,還可以指向未被宣告為const的變數。此時不能通過此指標變數改變該變數的值。
char c1=』a』;//定義字元變數c1,它並未宣告為const
const char *p;//定義了乙個指向常變數的指標變數p
p=&c1;//使p指向字元變數c1
*p=』b』;//非法,不能通過p改變變數c1的值;
c1=』b』//合法,沒有通過p訪問c1,c1不是常變數
注意:定義指向常變數的指標變數p並使它指向c1,並不意味著把c1也宣告為常變數,而只是在用指標變數訪問c1期間,c1具有常變數的特徵,其值不能改變,在其它情況下,c1仍然是乙個普通的變數,其值是可以改變的。
如果希望在任何情況下都不能改變c1的值,則應把它定義為const型,如:
const char c1 = 『a』;
(3)如果函式的形參是指向普通(非const)變數的指標變數,實參只能是指向普通(非const)變數的指標,而不能指向const變數的指標。
以上介紹的是指向常變數的指標變數,指向常物件的指標變數的概念與使用與此類似,只要把「變數」換成「物件」即可。
time * const p; //指向物件的常指標變數 p
的值(p的指向)不能修改
const time *p; //指向常物件的指標變數,p
指向的類物件的值不能通過p來修改
將指標變數宣告為const型,這樣指標變數始終保持初值,不能改變,即其所指向不變。
timet1(10,12,15),t2;
time *const ptr1; //const
位置在指標變數名前面,指定ptr1是常指標變數
ptr1 =&t1; //ptr1
指向物件t1,此後不能再改變指向
ptr1 =&t2; //
錯誤,ptr1不能改變指向
注意:指向物件的常指標變數的值不能改變,即始終指向同乙個物件,但可以改變其所指向物件(如t1)的值。
什麼時候需要用指向物件的常指標呢?如果想將乙個指標變數固定位址與乙個物件相聯絡(即該指標變數始終指向乙個物件),可以將它指定為const型指標變數。這樣可以防止誤操作,增加安全性。
往往用常指標作為函式的形參,目的是不允許在函式執行過程中改變指標變數的值,使其始終指向原來的物件。
指向常物件的指標最常用於函式的形參,目的是在保護形參指標所指向的物件,使它在執行過程中不被修改。
int main()
void fun(const time *);//函式宣告,形參是指向常物件的指標變數
time t1(10,13,56);
fun(&t1); //實參是物件t1的位址
return 0;
void fun(const time *p) //定義fun函式
p->hour = 18;//錯誤
cout
C 繼承 共有,私有,保護
公有繼承 public 私有繼承 private 保護繼承 protected 是常用的三種繼承方式。1.公有繼承 public 公有繼承的特點是基類的公有成員和保護成員作為派生類的成員時,它們都保持原有的狀態,而基類的私有成員仍然是私有的,不能被這個派生類的子類所訪問。2.私有繼承 private...
C 繼承 共有,私有,保護
公有繼承 public 私有繼承 private 保護繼承 protected 是常用的三種繼承方式。1.公有繼承 public 公有繼承的特點是基類的公有成員和保護成員作為派生類的成員時,它們都保持原有的狀態,而基類的私有成員仍然是私有的,不能被這個派生類的子類所訪問。2.私有繼承 private...
C 繼承 共有,私有,保護
公有繼承 public 私有繼承 private 保護繼承 protected 是常用的三種繼承方式。1.公有繼承 public 公有繼承的特點是基類的公有成員和保護成員作為派生類的成員時,它們都保持原有的狀態,而基類的私有成員仍然是私有的,不能被這個派生類的子類所訪問。2.私有繼承 private...