最近在看C++Primer第5版,先前已經看過第4版,但是發現第5版在整個知識布局與個別知識的講解上跟第4版差別還是挺大的,尤其是新增了C++11的內容,正如孟巖老師在第5版前言中所講:“現在能夠以新的開發實踐的人是鳳毛麟角,如果能夠純熟的運用C++11的新特征、新機制,那麼就能夠形成一種簡潔優雅的C++編程風絡,開發會變得更高效,更高質”。
所以正好借助第5版來重新學習鞏固C++的知識。《C++的那些事》這個系列,將會以知識碎片的形式記錄我在學習過程中一些知識重點。
個人身為C++菜鳥,自然是沒有任何經驗之談,這裡摘錄C++Primer5ED前言裡一些個人以為很有道理的觀點。
1,學習語言的一個境界是把自己想象成編譯器。
2,使用C++語言的“兩面性”觀點。C++正在走向完美,所以,C++語言值得學習(甚至研究)。這些知識可以成為一切編程的基礎。然而在實踐中,不必全面地使用C++語言的各種特性,而應根據工程項目實際情況,適當取捨(譬如動態類型信息、虛擬繼承、異常等特性的使用)。通常只鼓勵使用C++語言的一個子集。
關於第2點,個人是相當的認同,C++的一些高級特性,確實只有在一些庫例程中才有體現,一般工程開發中很少能用得上。所以在學習C++的時候,開始不必對一些語法細節或高級技法過於追求完美,可以先知其大概,在以後工作學習中再逐漸加深認識。
1,任何一門編程語言都是由一些基本組件來構成,C++也是一樣。
C++的另外一點就是編程思想,通過類引入了面向對象編程的思想,再通過模板等技術引入了通用或泛型編程的思想。
這本篇文章中,主要是關於前2點的內容:對象和對象的類型。
2,類型是程序設計中最基本的概念,一種類型不僅定義了數據元素的內容,還定義了這類數據上可以進行的運算。內置的數據類型如此,用戶自定義的類型也是如此,。
3,程序所處理的數據都保存在變量中,而每個變量都有自己的類型。
4,執行算術運算時,數據類型的選擇
5,在使用無符號數作為循環的索引時,注意unsinged int 與 int類型間的隱式數據轉換,如下面的程序,可能實際並不是你想要的結果。
(unsigned u = ; u > = ; -- - std::cout << u << }
6,C++11定義了一種新的指針字面值nullptr,用於取代C語言中預編譯常量NULL
7,注意C++中初始化與賦值的區別,這一點在類的定義中更為明顯。初始化不是賦值,初始化的含義是創建變量時賦予其一個初始值,而賦值的含義是把對象的當前值擦除,而以一個新值來替代。
8,引用和指針都是C++定義的復合類型,引用與指針的定義是由一個基本數據類型和緊隨其後的一個聲明符列表組成。
1)引用是給變量另起了一個名字,當定義引用時,程序把引用和它的初始值綁定在一起,而不是將初始化拷貝給引用。一旦初始化完成,程序把引用和它的初始值對象一直綁定在一起。因為無法令引用重新綁定到另外一個對象,因此引用必須初始化。
實際上引用的本質是指針,而且是一個常量指針,占用4個字節的空間。
2)與引用不同的是,指針本身是一個對象,允許指針拷貝和賦值,而且在指針的生命周期內,它可以先後指向幾個不同的對象。
9,常量表達式是指值不會改變並且在編譯過程中就能得到計算結果的表達式。
在一個復雜的系統中,很難分辨一個初始值到底是不是常量表達式。C++11允許將變量聲明為constexpr類型以便由編譯器來驗證變量的值是否是一個常量表達式。聲明constexpr的變量一定是一個常量,而且必須用常量表達式初始化:
constexpr mf = constexpr limit = mf + constexpr sz = size();
用constexpr修飾的指針說明是常量指針,它本身在定義初始化後不可以再更改指向,但是所指的對象可以是個變量。
10,注意指針、常量和類型別名在一塊的時候:
typedef * pstring; pstring cstr=; pstring *ps;
11,auto類型
C++11引入auto類型說明符,用它就能讓編譯器替我們去分析表達式所屬的類型。編譯器是通過初始值來推算變量的類型,顯然,auto定義的變量必須有初始值。
如果我們用一個引用類型去初始化一個auto類型時,得到的類型將是引用對象的類型:
i = , &r = auto a = r;
其次auto一般會忽略掉頂層的const,同時底層的const則會保留下來。
ci = i, &cr = auto b = ci; auto c = cr; auto d = &i; auto e = &ci;
如果希望auto推斷出的是auto類型是一個頂層的const,需要明確指出:
auto f = ci;
12,decltype類型指示符
有的時候想從表達式的類型推斷出要定義的變量的類型,但是不想用該表達式的值初始化變量,為些C++11引入了第二種類型說明符decltype,它的作用是選擇並返回操作數的數據類型。
decltype(f()) sum = x;
decltype在處理頂層const和引用的方式與auto不同。
ci = , &cj = decltype(ci) x = ; decltype(cj) y = x; decltype(cj) z;
delctype((variable))(注意是雙層括號)的結果永遠是引用,而decltype(variable)結果只有當variable本身就是一個引用時才是引用。
13,標准庫定義了2種非常重要的抽象類型,一種是string用來支持可變長的字符串;另一種是vector表示可變長的集合。
14,對於string和vector對象都可以有多種方式初始化,這取決於類的定義,但一般直接初始化和拷貝初始化都是存在的。如果使用等號“=”初始化一個變量,實際上執行的是拷貝初始化,編譯器把等號右側的初始值拷貝到新創建的對象中去。與之相反,如果不使用等號,則執行的是直接初始化。
s1 = ; s2(); s3(, );
15,string類型和標准庫容器類型都提供了一種size_type類型,它是一個無符號的值,而且足夠存放下任何容器或string對象的大小。而在C++11中,我們可以用auto或decltype來讓編譯器自動推斷出這種類型,而不用寫很長的代碼,比如:
vector<vector<>>::size_type i; decltype(dvv.size()) i; auto i = dvv.size();
16,C++11中另外為vector對象提供了一種列表初始化的方法,此時,用花括號括起來的0個或多個初始化元素值被賦予vector對象。但是值得注意的是:如果用的是花括號,可以表述成我們想列表初始化該vector對象。也是就是,初始化過程會盡可能地把花括號內的值當成是元素初始化的列表來處理,只有在無法執行列表初始化時才會考慮其他初始化方式。
vector<> v1{}; vector<> v2(); vector<> v3{}; vector<> v4{,};
17,數組,與vector類似,數組也是存放類型相同的對象的容器,這些對象本身沒有名字,需要通過其所在的位置訪問。而且數組是在定義的時候就分配了大小,中間不能變化,這就給使用時帶來很大的不便。
18,指針其實與迭代器具有相同的功能,在遍歷數組的情況下,用指針操作就像在容器上使用迭代器一樣。類似於容器類型的begin和end迭代器,C++11定義了兩個函數begin和end用來獲取序列的首指針和尾指針,這兩個函數定義在iterator頭文件中。
ia[]={,,,,,,,,, *beg = begin(ia); *end = end(ia);