C++核心知識點整理——基礎知識
尊重函式介面,儘量不作內部改動
C++程式碼語句分為:內建型別,名字,變數,操作符,標量,字串,預處理指示(如#include)等
C++中定義類來組織資料結構
標準庫的標頭檔案用尖括號 < > 括起來,非標準庫的標頭檔案用雙引號 ” ” 括起來。
物件是記憶體中具有型別的區域。
在C++中,初始化和賦值是兩種不同的操作。
內建型別變數在任何函式外如不初始化,系統都會初始化為0,在函式體內如不初始化則可能發生錯誤(除了用作左運算元)。
有些類型別有預設建構函式,因此定義其物件時可以不顯式地提供初始化。
在 C++ 語言中,變數必須且僅能定義一次,而且在使用變數之前必須定義或宣告變數,宣告變數不分配記憶體,因此聲名不初始化,若宣告同時初始化則視為定義。
extern
要理解的重要概念是引用只是物件的另一名字,初始化是指明引用指向哪個物件的唯一方法。如int val = 1024;int &refval = val; //refval是val的引用,當int I = refval;相當於int I = val;引用是一種複合型別。
enum是列舉關鍵字enum Points{point2d,point2w,point3d,point3w};表示花括號內成員預設為0,1,2,3都是const變數,用來初始化列舉成員的值必須是一個常量表達式,但列舉型別的物件的初始化或賦值,只能通過其列舉成員或同一列舉型別的其他物件來進行。Points是一個列舉型別。
C++ 中,通過定義類來自定義資料型別。類定義了該型別的物件包含的資料和該型別的物件可以執行的操作。介面(interface)和實現(implement)。
定義變數和定義資料成員存在非常重要的區別,類中定義的變數稱為資料成員,當定義資料成員時,只能指定該資料成員的名字和型別。類不是在類定義裡定義資料成員時初始化資料成員,而是通過稱為建構函式的特殊成員函式控制初始化。
C++中也可以使用struct關鍵字來定義類,它與class的區別在於類中第一個訪問標號前的成員預設為public,而class預設為private。注意private只是對於類外語句呼叫許可權而言,在類內部成員函式可以隨意呼叫private成員。
標頭檔案用於宣告而不是定義,因為定義只可以出現一次,而標頭檔案在多個原始檔中出現,所以只用於宣告。對於標頭檔案不應該含有定義這一規則,有三個例外。標頭檔案可以定義類、值在編譯時就已知道的 const 物件和 inline 函式。標頭檔案中含有支援分別編譯所需的類定義及變數和函式的宣告。
C++前處理器是在編譯之前執行帶有預處理標誌#的程式,如#include指示允許兩種形式<>和“”,前者表示標準庫標頭檔案,後者表示自定義標頭檔案。#define前處理器變數,通常全部大寫字母。可以用:
#ifndef SALESITEM_H
#define SALESITEM_H
// Definition of Sales_itemclass and related functions goes here
#endif
首先第一句判斷SALESITEM_H是否定義,若沒有則第二句定義該前處理器變數,直到#endif結束。若第一句判斷出SALESITEM_H已經定義,則忽略後面的內容。此預處理命令可以用於避免標頭檔案在被程式多次包含時內部定義的類被反覆定義,引起編譯錯誤。
String型別的輸入操作符<<:忽略開頭的空白符(空格,製表符,換行符等),讀到該字串第一次出現空白符終止。字串字面值包含一個額外的空字元用於結束字串,所以“string”包含7個字元。
先初略地瞭解一下vector和iterator的概念,以後用到時深入學習,vector容器是一種型別,vector ivec表示儲存int物件的類模板。Iterator也是一個類,vector::iterator iter;表示由vector定義的迭代器型別物件iter,用於遍歷容器中的元素。使用它們之前必須在檔案頭包含並using宣告。
C++ primer第三章介紹了幾個常用的標準庫類:vector,string,iterator和bitset。
C++ 語言提供了兩種類似於 vector 和迭代器型別的低階複合型別——陣列和指標。現代 C++ 程式應儘量使用 vector 和迭代器型別,而避免使用低階的陣列和指標。設計良好的程式只有在強調速度時才在類實現的內部使用陣列和指標。
陣列定義的型別名可以是任意內建資料型別或類型別,陣列元素可以是除了引用之外的任意複合型別。陣列的維數必須用值大於等於1的常量表達式定義。此常量表達式只能包含整型字面值常量、列舉常量或者用常量表達式初始化的整型 const 物件。非 const 變數以及要到執行階段才知道其值的 const 變數都不能用於定義陣列的維數。
與vector不同,陣列不允許用另一個數組賦值和初始化,陣列一經定義就不允許再新增新元素。
指標的定義:int *p;從右往左讀,定義p為一個指向int型別物件的指標變數。一個有效的指標必然是以下三種狀態之一:儲存一個物件的地址;指向某個物件後面的另一個物件;0值。避免使用未初始化的指標。
指標的算術操作:加減整型數值。與迭代器的算術操作實現方式相同。對指標解引用,可得到它所指向的物件的值:*p。在表示式中使用陣列名時,實際上是使用了指向該陣列第一個元素的指標,注意陣列名與指標變數的等價性。
C++ 允許計算陣列或物件的超出末端的地址,但不允許對此地址進行解引用操作。如:
const size_t arr_size = 5;
int arr[arr_size] = {0,1,2,3,4};
int *p = arr;
int *p2 = p + arr_size;
p2儲存的是陣列arr_size超出末尾的地址。
指向const的指標理解為:“自以為指向const的指標”,當一個指標定義時指向const物件,它就會認為自己是一直指向const物件的指標,即使它後來指向一個非const物件,也不能通過引用修改該物件的值。但可以通過重新定義一個指向該物件的指標修改值。在實際的程式中,指向 const 的指標常用作函式的形參。
另外還有const指標,如int *const p = &val;const指標若指向const物件,則限制一切改動的行為。
位操作符<<和>>分別代表二進位制數各位整體左移或右移右運算元位數。如int 12>>1;表示1100變為0110(6)。
賦值操作返回左值,具有右結合性。
j = i++與j = ++i的區別,前者自增操作符返回初值,後者返回自增後的值。儘量使用前置自增。
C++語言為包含點操作符和解引用操作符的表示式提供了一個同義詞:->箭頭操作符。
如:sales_item *sp = &item1; (*sp).same_isbn(item2);等效於sp->same_isbn(item2);
運用new和delete語句建立和撤銷動態記憶體,即可建立和釋放動態陣列也可建立和釋放單個物件,如:int *pi = new int; 表示在自由儲存區(堆)分配了一個整型物件,並返回該物件的地址,並用該地址初始化指標pi。
又如:int *pi = new int(20); 同時初始化該整型物件為20。string *ps = new string; string的預設建構函式將其初始化為一個空字串。若要顯式初始化非預設初始化的物件,可寫為:
int *pi = new int();在型別名後面加圓括號表示初始化為空值,這樣做可以避免因未初始化帶來的錯誤。
delete pi; 表示釋放pi所指向記憶體,但pi中儲存的地址仍存在,此時pi成為懸垂指標,易發生錯誤,應立即將pi置0,以顯示它未指向任何物件。使用delete刪除非自由儲存區的記憶體是不合法的。
對於const物件,須返回const物件地址,如:const int *pi = new const int(1024);
C風格字串是一個const char型字元陣列:const char *ps = “C style”; 以空字元為結束位;C++標準庫用string型別重新定義了字串,更加簡單直觀: string ps(“characters string”);
儘量使用string類來定義字串。
C++編譯器在隱式型別轉換時會盡可能防止精度損失。強制型別轉換的一般格式為:
cast-name(expression) ; 其中cast-name是要強制轉換的方式,如static_cast;
const_cast 等,type是欲轉換為的型別。儘量避免使用強制型別轉換。
switch語句每個case後要加break,否則程式只會跳過後續的case標號繼續執行case標號內的內容。若要在case中定義變數,則使用花括號限定變數的使用範圍。
C++異常處理關鍵字:throw和try{}catch{}catch{}…;瞭解一下,throw用於退出程式碼塊,轉向異常處理。try一段程式碼,catch其中的語句,作相應處理。標準庫定義的異常處理類都在stdexcept標頭檔案中。
函式形參使用引用修改實參的值安全而方便,儘量不使用指標。
函式中如不需要修改實參的值,則統一使用const形參引用,如:下列程式在s中查詢c字元:
string::size_type find_char(string &s,char c){
string::size_type i = 0;
while(i != s.size() && s(i) != c )
++i;
return i;
}
若呼叫此函式find_char(“string”,’s’); 則出現編譯錯誤,字串和字元字面值是右值,可以通過const string &s 引用,此處字面值常量先隱式轉化為一個臨時const物件再初始化const string &s。
區分int *matrix[10 ]; 和int (*matrix) [10];前者表示包含10個指標的指標陣列,後者表示指向含有10個int型元素的陣列的指標。 陣列下標優先順序大於指標操作符。
int main(int argc, char *argv[]) 中,argv是一個c風格的字串陣列,char *argv[]相當於char **argv,argc儲存argv的字串個數。
左值可出現在賦值語句的右側或左側,而右值只能出現在賦值號的右側。函式返回值用於初始化在呼叫函式處建立的臨時物件。因此,函式可返回引用作為左值:const string &shorterString(const string &s1,const string &s2),形參和返回型別都是引用。但不要返回區域性物件的引用,同樣,可以返回指標,但不能返回區域性物件的指標,否則會成為懸垂指標。
遞迴函式是直接或間接呼叫自身的函式,必須要有中止條件,否則會無限迴圈。如:定義一個遞迴函式求取1×2×3……100的值:
int f(int val){
if (val > 1) return f (val-1)*val;
return val;
}
函式宣告可省略形參名,一般在宣告階段提供預設實參:string screenInit(string::size_type width = 80, string::size_type height = 20, char background = ‘c’); 當呼叫函式時,初始化值會從左向右覆蓋預設實參值,因此應將最有可能變更的預設實參放在最左邊。函式宣告一般整理放在標頭檔案中,在原始檔中包含標頭檔案。
函式中,每個名字都有作用域,每個物件都有生命期,形參和區域性變數的生命期在函式呼叫過程中,它們的名字作用域限於函式塊中從定義到快結束之間。若我們希望一個區域性物件在函式呼叫結束後仍然具有生命,則可以定義靜態區域性變數:static關鍵字。
定義inline行內函數是為了編譯器在處理函式時按照函式塊內語句展開,節省直接處理函式帶來的花銷。在普通函式前加inline關鍵字,並只能在標頭檔案中定義。
類的成員函式在類內宣告,可以在類外定義,也可以在類內定義。其形參表包含一個隱式形參this指標,初始化為呼叫該成員函式的物件地址,如:在Sales_item類中定義的成員函式:
bool same_isbn(const Sales_item &rhs) const {
return isbn == rhs.isbn;
}
在呼叫total.same_isbn(trans)時,花括號前的const表明隱式指標this是一個指向total物件的const Sales_item* 型別的指標,該函式稱為常量成員函式。return isbn == rhs.isbn相當於:
return this->isbn == rhs.isbn; 函式體中可以顯式地使用後者語句,但沒有必要。
建構函式是一種特殊的成員函式,用於初始化類,同名建構函式可以過載,由不同數目或型別的形參表區分。建構函式沒有返回型別,和類同名。
通常,我們將類放在與類同名的標頭檔案中定義,而成員函式放在與類同名的原始檔中定義。
每個版本的過載函式應在同一個作用域中宣告,區域性同名函式會覆蓋全域性函式而不是過載。