1:區分C++中的術語聲明、定義、初始化的概念
? ? ? ? ?聲明(declaration):告訴編譯器某個東西的名稱和類型,但略去其他細節(可以出現多次,編譯器不分配內存)。
? ? ? ? ?定義(definition):提供編譯器一些聲明式所遺漏的細節,編譯器此時為對象分配內存(注意內置類型和自定義類型定義的變量都叫做對象,變量有且僅有一次定義)。
? ? ? ? ?聲明和定義:1)extern關鍵字區分;2)聲明中出現初始化式,當做定義,即使出現extern關鍵字;3)帶{ }的是定義,其他的是聲明;
? ? ? ? ?引申:頭文件中不要放入變量定義,容易導致重復定義錯誤;可以使用static關鍵字把變量定義限制在源文件作用域內;
頭文件中一般不放實體定義,但有三個例外:const常量定義;類的定義;inline函數;
? ? ? ? ?如何區分聲明和定義,可以參考這篇文章:https://blog.csdn.net/sjxbf/article/details/6310150;
? ? ? ? ?初始化(initialization):給予對象初值的過程;用戶自定義函數對象初始化由構造函數執行,default構造函數可以調用不帶任何實參(通常有兩種情況:情況1:沒有參數;情況2:每個參數都有缺省值)
2:explicit關鍵字:用來阻止執行隱式類型轉換,但仍可以執行顯示類型轉換;??
? ? ? ? ?explicit主要用于單個參數的類構造函數中,如果出現多個參數,explicit關鍵字失效,除了一種情況例外,就是除了第一個參數,其他參數都有默認缺省值時,explicit依然有效;
3:copy構造函數和copy assignment操作符
? ? ? ? ?copy構造函數:用于同型對象初始化自我對象時(初始化);
? ? ? ? ?copy assignment操作符:用于從一個同型對象中拷貝其值到自我對象(賦值操作時);
? ? ? ? copy構造函數的幾個運用場所:1)初始化時,如Wiget w2;Wiget w1(w2);2)初始化賦值時;Wiget w2=w1;3)類對象值傳遞的時候;
4:命名習慣:養成選用所實現目標的英文翻譯是個不錯的選擇,可以方便閱讀和修改;例如類中的左右操作數可以定義參數名稱為lhs(left-hand-side)和rhs(right-hand-side);
5:合理利用TR1(class template和function template,hash table,reference-counting smart pointers,regular expressions)和boost網站資源(提供可移植,同僚復審,源碼開發的C++程序庫)
6:C++語言是一個多重范型編程語言(multiparadigm programming language),一個同時支持過程形式(procedural)、面向對象形式(object-oriented)、函數形式(functional)、泛型形式(generic)、元編程形式(metaprogramming)的語言。C++可以視為由相關語言組成的聯邦和非單一語言,其中每個次語言中,各種守則與通例都傾向于簡單直觀易懂和容易記住,關鍵是次語言之間切換過程。次語言分為一下幾個方面:
? ? ? ? ?1)C:區塊(blocks),語句(statement)、預處理器(preprocessor)、內置數據類型(bulit-in data types)、數組(arrays)、指針(pointers),但在高效編程時C語言卻沒有C++的功能如模板(template)、異常(exceptions)、重載(overloading)等;
? ? ? 2)object-oriented C++:classes(構造和析構函數)、封裝(encapsulation)、繼承(inheritance)、多態(polymorphism)、virtual函數(動態綁定);類的三個核心:封裝,繼承,多態;
? ? ? ? ? 3)Template C++。C++泛型編程的思想;
? ? ? ? ?4)STL。STL是個template程序庫,它將容器(containers)、迭代器(iterators)、算法(algorithm)以及函數對象(function objects)聯合起來;
7:盡量使用const,enum,inline替代#define(用編譯器替代預處理器)
? ? ? ?#define 是在程序預處理的時候執行的,它不被看成語言的一部分,也就可能會出現所使用的宏變量沒有進入到記號表(symbol table)中,修改的方式:用一個常量替代宏;另外使用const還有一個好處,系統會做類型檢查,同時完成類型的位數限制(發生在float中)如#define pi 3.141592653;和const float pi=3.141592653得到的結果不一樣,相比之下,用const會產生更小量的碼值;
? ? # define并不注重作用域(scope)的概念,一旦定義,除非#undef,后面都有效,因此不能用于定義類的專屬常量,也不能提供任何封裝性;但const卻能提供這樣的功能,const可以將常量的作用域限制在classes內,讓它成為class的一個成員,為了確保常量最多有一份實體,你得設置為static成員,如:
?class gameplayer{
private:
? ? ? ? ? static const int Numturns=5;//常量聲明式,和以往static聲明和定義不一樣,這種成為in-class初值設定? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//只允許對整形變量使用,還和C++編譯器是否支持有關
? ? ? ? ?//static const double score;一般的static常量聲明
? ? ? ? ? int score[Numturns];//使用常量
};
const double gameplayer::score=95.0;//static常量定義
類中的Numturns是一個聲明式而非定義式,C++要求你對你所使用的任何東西提供定義式,但如果它是類的一個專屬常量而且還是整形類型(int,char,bool),則需要特殊處理。只要不取地址,可以直接聲明并使用,不需要提供定義式;但如果你取了某個類專屬常量的地址或者編譯器需要看到一個定義式,則你必須提供定義式:const int gameplayer::Numturns;//聲明時已經賦值,定義則不需要賦值(放入實現文件而非頭文件中)
? ? ? 如果編譯器不允許static整數型class常量完成in-class初值設定,可以用the enum hack補償方法完成同樣功能;
?class gameplayer{
private:
? ? ? ? ? enum {Numturns=5};
? ? ? ? ? int score[Numturns];//使用常量
};
? ? 引申:enum hack知識:enum hack的行為有點像#define而非const,因為其和#define同樣不能取地址,而const可以,如果不想讓別人獲得一個指針或者引用指向你的某個常量,enum可以實現這個功能;此外,enum hack是template metaprogramming(模板元編程)的基礎技術;
? ? ?#define常見的誤用情況是用它實現宏(macros),宏看起來像函數,卻不會招致函數調用帶來的額外開銷,這也是錯誤的根源,如:
? ? ?#define CALL_WITH_MAX(a,b)? f((a)>(b)?(a):(b))
? ? ?int a=5;b=0;CALL_WITH_MAX(++a,b);//a被累加兩次? ?
? ? ?解決方案:使用template inline函數,會在被調用處代碼展開,省去參數壓棧、棧幀開辟和回收、結果返回;同時會做安全檢查或者自動類型轉換;
? ? ?inline void callwithmax(const int&a,const int&b){f(a>b?a:b);}
? ? ?結論:對于單純變量,最好以const對象或者enum替換#define;
? ? ? ? ? ? ? ?對于函數的宏,最好改用inline函數替換#define;
8:盡量多使用const:它允許你指定一個語義約束,而編譯器執行這個約束,它允許你告訴編譯器和其他程序員某值應該保持不變;
? ? ?1)STL迭代器中:聲明迭代器為const,則迭代器指針不能變化,聲明迭代器內容為const,如const_iterator,則迭代器內容不可變;
? ? 2)在函數聲明式中,const可以和返回值、各參數、函數本身產生關聯;令函數返回一個常量值,往往可以降低因用戶錯誤而造成的意外,同時不放棄安全性和高效性,如無意中輸入的if(a*b=c)操作,可通過返回值設為const來避免這個錯誤;
? ? 3)const成員函數:設置成const好處(方便理解;使操作const對象成為可能,編程更加高效);如果兩個成員函數只是常量(constness)不同,可以被重載,這是C++的一個特性;
? ? ? 引申:如果函數返回值是一個內置類型,那么改動函數返回值是不合法的,C++以by value返回對象意味著改動的只是對象的一個副本,而不是本身,這并不是我們希望的,因此需要把其設置為引用;
? ? ?成員函數是const意味著什么?有兩種說法:bitwise constness(physical constness)和logical constness;bitwise constness認為成員函數不改變對象中的任何成員變量的任何一個bit,因此編譯器只需要尋找有無成員變量賦值操作,bitwise constness這一個特性是C++對常量性(constness)的定義,因此const成員函數不能改變對象中的任何non-static成員變量,但是在指針的作用下這個const會發生問題,如:
? ? const mystring a("hello world"); char *pc=&a[0];*pc='j';//改變了const特性
? ?logical constness:一個const成員函數可以修改它處理對象內的某些bits,但只有在客戶端偵查不出的情況下才能如此,如何解除編譯器中的bitwise constness特性呢?可以使用const相關的一個關鍵字mutable,mutable可以釋放non-static成員變量中的bitwise constness約束;
?4)在const和non-const中避免重復的方法:用其中一個調用另外一個,通過我們采用常量性轉移,如;
class textblock{
public:
? ? ? const char&operator[](std::size_t position)const? ? ?//const 成員函數不變
? ? ?{
? ? ?}
? ?char& operator[](std::size_t position)
? ?{
? ? ? return const_cast<char&>(//去除返回值const
? ? ? ? ? ? static_cast<const textblock&>(*this)//this指針添加const
? ? ? ? ? ? ? [position]);
? ?}?
};
? ? 采用non-const調用const版本的函數來避免重復,而不是相反的做法,原因:const變為non-const過程中可能會使得const變量發生變化,這與我們預期不合;而non-const本身就是可變的,不用擔心這個現象的發生;
結論:聲明const可以幫助編譯器偵查出錯誤的用法;
? ? ? ? ?編譯器強制實行bitwise constness,但可以通過指針方式改變;
? ? ? ? const和non-const成員函數有著實質等價的實現時,可以用non-const調用const來避免代碼重復;
? ??
? ? ? ? ?