A.基礎問題
1.過分注釋(對注釋的更新與代碼不一緻), 字面常量(幻數,看不出意義, 可用 enum 或 const 解決).
2.全局變量.
<--幼稚的消息傳遞機制
(1) 可用函數調用得到 (2) 可用一個類單例得到
3.未能區分函數重載和形參預設值.
4.引用:
引用隻是其初始化物的别名.引用是沒有位址的,甚至不占有任何存儲空間. 由于無位址,聲明指向引用的指針,引用的引用都是不合法的。
注:沒有空引用,也無類型為void的引用。任何可作為左值的都可被引用 .
指向數組的引用 : int ( & array )[10] ; <=保留了尺寸
int f(double) ; f(18.6) <=> (*f)(18.6)
5.對常量的誤區:
const <- 常量, 10 <- 字面量
const volatile <= 隻限制對const 的直接修改,間接修改是可行的,不會引發未定義的行為(??)
6.無視語言的精妙之處:
(1) bool r = a < b ; <= never use if in this case !!! 不要用 if
(2) 位屏蔽算法: b & m & ((b & m) - 1) ;;結果為0則沒選或隻選一個
(3) 如果條件運算符表達式兩個選擇結果都為左值,那麼這個表達式就是左值 :
a<b ? c: d < e ? d: e = val();
(4) 内建索引運算符 :
p[-2] <=> *(p + (-2)) <=> * ( -2 + p) <=> (-2)[p]
(5) switch <= 隻管入口,如果要直下,最好加上注釋.
另:case 可不在同一級,case 可在任何地方.
7.空指針: #define NULL ((char * )0) / ((void*)0) / 0
故,最好用 0 : C * cp = 0;
8.無視習慣用法:
X ( const X &) ; X & operator = ( const X &)
文法問題:
1. int * ip = new int(12) ; //僅初始化了一個整數,并非一個數組
int a[12] ; int a(12);
vector <int> iv(12); <--下标通路,不用顯式回收
注:最好的記憶體申請形式就是根本不做這個記憶體申請,直接用标準庫中元件.
2. 不定的求值順序:
(a=12) + a
a=( p() + q() ) + r();
<---可以有六種順序
Thing * pThing = new ( Thing ( initVal() ) ;
<---可以肯定 operator new 會比 Thing 類型構造函數先調用(先配置設定空間,後初始化.)
return f(), g(), h();
a = f() + g() ? p() : q(); <=> a = ( f() + g() ) ? p() : q();
左結合性:該運算符先會綁定左邊那個參數;
-> 有很高的優先級, 但是低于 ()
++ 高于 -> *
3. for 語句
if ( char * theName = lookUp( name )){ }
for ( int i = 0 ; ; ){ }
4. 取大優先: list < vector < string >> lovos; // >>之間要有空格!!,不然會被當作...
5.聲明飾詞: 先連接配接,再量化,最後類型
int const * const * pp1; extern const int;
6. 是函數還是對象: String x(); <-函數聲明,多義 String x(void)
String * sp1 = new String () ; <=> sp1 = m new String; (後面較好)
7.自反初始化:
int var = 12;
{
double var = var; // 未定義... 對枚舉則可行
}
8. 運算符名字查找的反常行為:
中序文法調用:
函數調用文法: 名字查找遵從标準形式
9 operator -> : 重載版本是一進制的,并且它必須傳回一個可用 -> 通路其成員的對象.
預處理問題:
1. #define 的作用域并未被限制在名字空間中;
盡量不要用 #define 調用的僞函數. ---> static const ,函數對象,operator()
2. 如果調試代碼在最終生成的可執行檔案中根本不存在,那麼編譯器會剔除無用代碼...
if( false ) { }
避免用 #if 作為源代碼的控制.
3. assert 是一個宏 <--- 即也是一個僞函數. #define assert(e) ((void) 0)
----->是以,當使用 assert 時,不要在 e 中使用函數調用.