下面的這些要點是對所有的C++程序員都適用的。我之所以說它們是最重要的,是因爲這些要點中提到的是你通常在C++書中或網站上無法找到的。如:指向成員的指針,這是許多資料中都不願提到的地方,也是經常出錯的地方,甚至是對一些高級的C++程序員也是如此。 這裏的要點不僅僅是解釋怎樣寫出更好的代碼,更多的是展現出語言規則裏面的東西。很顯然,它們對C++程序員來說是永久的好資料。我相信這一篇文章會使你收穫不小。 首先,我把一些由不同層次的C++程序員經常問的問題歸到一起。我驚奇的發現有很多是有經驗的程序員都還沒意識到 .h 符號是否還應該出現在標準頭文件中。 要點1: <iostream.h> 還是 <iostream>? 很多C++程序員還在使用<iostream.h>而不是用更新的標準的<iostream>庫。這兩者都有什麼不同呢?首先,5年前我們就開始反對把.h符號繼續用在標準的頭文件中。繼續使用過時的規則可不是個好的方法。從功能性的角度來講,<iostream>包含了一系列模板化的I/O類,相反地<iostream.h>只僅僅是支持字符流。另外,輸入輸出流的C++標準規範接口在一些微妙的細節上都已改進,因此,<iostream>和<iostream.h>在接口和執行上都是不同的。最後,<iostream>的各組成都是以STL的形式聲明的,然而<iostream.h>的各組成都是聲明成全局型的。 因爲這些實質上的不同,你不能在一個程序中混淆使用這兩個庫。做爲一種習慣,在新的代碼中一般使用<iostream>,但如果你處理的是過去編寫的代碼,爲了繼承可以用繼續用<iostream.h>舊保持代碼的一致性。 要點2:用引用傳遞參數時應注意的地方 在用引用傳遞參數時,最好把引用聲明爲const類型。這樣做的好處是:告訴程序不能修改這個參數。在下面的這個例子中函數f()就是傳遞的引用: |
void f(const int & i); int main() { f(2); /* OK */ } |
這個程序傳遞一個參數2給f()。在運行時,C++創建一個值爲2的int類型的臨時變量,並傳遞它的引用給f().這個臨時變量和它的引用從f()被調用開始被創建並存在直到函數返回。返回時,就被馬上刪除。注意,如果我們不在引用前加上const限定詞,則函數f()可能會更改它參數的值,更可能會使程序產生意想不到的行爲。所以,別忘了const。 這個要點也適用於用戶定義的對象。你可以給臨時對象也加上引用如果是const類型: |
struct A{}; void f(const A& a); int main() { f(A()); // OK,傳遞的是一個臨時A的const引用 } |
要點3:“逗號分離”表達形式 “逗號分離”表達形式是從C繼承來的,使用在for-和while-循環中。當然,這條語法規則被認爲是不直觀的。首先,我們來看看什麼是“逗號分離”表達形式。 一個表達式由一個或多個其它表達式構成,由逗號分開,如: |
if(++x, --y, cin.good()) //三個表達式 |
這個if條件包含了三個由逗號分離的表達式。C++會計算每個表達式,但完整的“逗號分離”表達式的結果是最右邊表達式的值。因此,僅當cin.good()返回true時,if條件的值纔是true。下面是另一個例子: |
int j=10; int i=0; while( ++i, --j) { //直到j=0時,循環結束,在循環時,i不斷自加 } |
要點4,使用全局對象的構造函數在程序啓動前調用函數 有一些應用程序需要在主程序啓動前調用其它函數。如:轉態過程函數、登記功能函數都是必須在實際程序運行前被調用的。最簡單的辦法是通過一個全局對象的構造函數來調用這些函數。因爲全局對象都是在主程序開始前被構造,這些函數都將會在main()之前返回結果。如: |
class Logger int main() |
全局對象log在main()運行之前被構造,log調用了函數activate_log()。從而,當main()開始執行時,它就可以從log文件中讀取數據。
|
void (*p[10]) (void (*)()); |
P是一個“由10個指針構成的指向一個返回void類型且指向另一個無返回和無運算的函數的數組”。這個麻煩的語法真是讓人難以辨認,不是嗎?你其實可以簡單的通過typedef來聲明相當於上面語句的函數。首先,使用typedef聲明“指向一個無返回和無運算的函數的指針”: |
typedef void (*pfv)(); |
接着,聲明“另一個指向無返回且使用pfv的函數指針”: |
typedef void (*pf_taking_pfv) (pfv); |
現在,聲明一個由10個上面這樣的指針構成的數組: |
pf_taking_pfv p[10]; |
與void (*p[10]) (void (*)())達到同樣效果。但這樣是不是更具有可讀性了! |
指向數據成員的指針 |
int * pi; |
定義一個指向爲int型的類的數據成員: |
int A::*pmi; //pmi是指向類A的一個int型的成員 |
你可以這樣初始化它: |
class A { public: int num; int x; }; int A::*pmi = & A::num; |
上面的代碼是聲明一個指向類A的一個int型的num成員並將它初始化爲這個num成員的地址.通過在pmi前面加上*你就可以使用和更改類A的num成員的值: |
A a1, a2; int n=a1.*pmi; //把a1.num賦值給n a1.*pmi=5; // 把5賦值給a1.num a2.*pmi=6; // 把6賦值給6a2.num |
如果你定義了一個指向類A的指針,那麼上面的操作你必須用 ->*操作符代替: |
A * pa=new A; int n=pa->*pmi; pa->*pmi=5; |
指向函數成員的指針 它由函數成員所返回的數據類型構成,類名後跟上::符號、指針名和函數的參數列表。舉個例子:一個指向類A的函數成員(該函數返回int類型)的指針: |
class A { public: int func (); }; int (A::*pmf) (); |
上面的定義也就是說pmf是一個指向類A的函數成員func()的指針.實際上,這個指針和一個普通的指向函數的指針沒什麼不同,只是它包含了類的名字和::符號。你可以在在任何使用*pmf的地方調用這個函數 |
func(): pmf=&A::func; A a; (a.*pmf)(); //調用a.func() |
如果你先定義了一個指向對象的指針,那麼上面的操作要用->*代替: |
A *pa=&a; (pa->*pmf)(); //調用pa->func() |
指向函數成員的指針要考慮多態性。所以,當你通過指針調用一個虛函數成員時,這個調用將會被動態回收。另一個需要注意的地方,你不能取一個類的構造函數和析構函數的地址。 要點7、避免產生內存碎片 要點8、是delete還是delete[] |
int *p=new int[10]; delete p; //錯誤,應該是:delete[] p |
上面的程序是完全錯誤的。事實上,在一個平臺上使用delete代替delete[]的應用程序也許不會造成系統崩潰,但那純粹是運氣。你不能保證你的應用程序是不是會在另一個編譯器上編譯,在另一個平臺上運行,所以還是請使用delete[]。 要點9、優化成員的排列一個類的大小可以被下面的方式改變: |
struct A { bool a; int b; bool c; }; //sizeof (A) == 12 |
在我的電腦上sizeof (A) 等於12。這個結果可能會讓你吃驚,因爲A的成員總數是6個字節:1+4+1個字節。那另6字節是哪兒來的?編譯器在每個bool成員後面都插入了3個填充字節以保證每個成員都是按4字節排列,以便分界。你可以減少A的大小,通過以下方式: |
struct B { bool a; bool c; int b; }; // sizeof (B) == 8 |
這一次,編譯器只在成員c後插入了2個字節。因爲b佔了4個字節,所以就很自然地把它當作一個字的形式排列,而a和c的大小1+1=2,再加上2個字節就剛好按兩個字的形式排列B。 |
要點10、爲什麼繼承一個沒有虛析構函數的類是危險的? 一個沒有虛析構函數的類意味着不能做爲一個基類。如std::string, std::complex, 和 std::vector 都是這樣的。爲什麼繼承一個沒有虛析構函數的類是危險的?當你公有繼承創建一個從基類繼承的相關類時,指向新類對象中的指針和引用實際上都指向了起源的對象。因爲析構函數不是虛函數,所以當你delete一個這樣的類時,C++就不會調用析構函數鏈。舉個例子說明: |
class A { public: ~A() // 不是虛函數 { // ... } }; class B: public A //錯; A沒有虛析構函數 int main() |
要點11、以友元類聲明嵌套的類 當你以友元類聲明一個嵌套的類時,把友元聲明放在嵌套類聲明的後面,而不前面。 |
class A { private: int i; public: class B //嵌套類聲明在前 { public: B(A & a) { a.i=0;}; }; friend class B;//友元類聲明 }; |
如果你把友元類聲明放在聲明嵌套類的前面,編譯器將拋棄友元類後的其它聲明。 |
你最需要注意的11條要點for C++[轉]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.