雖然,有一定的c++基礎(因為本科就學會了這一種,哈哈),但是還是決定系統的讀一遍這本書(之前有零星看過數次。。汗)。
留作自己以後參考。(內容會不定期更改,不斷學習(此處應為長音~~))
大部分都是自己掌握的不夠扎實的地方和以前沒有注意的一些細節。
書中好多地方,詳述了知識出現的緣由,最起碼是指出為了解決什麼問題而出現的!!
前言部分
1.“...大量使用了前後交叉引用..."(挺適合自己的,我想知道原文是怎麼的,希望以後有機會對原版的時候補充~)
2.作者對本書的定位是本指南性讀物。。。
3.作者假定讀者已經掌握了一種現代結構化語言!
4.學到足夠的知識去創建自己的抽象(於第三到第五部分討論)
快速入門部分
1.‘操作系統通過main函數返回的值來確定程序是否成功執行完畢。’
2.IDE (Integrated Development Environment) 集成開發環境(書中不介紹~~)
3.注釋comment
4.讀入未知數目的整數輸入(利用 輸入操作符>> 返回其左操作數實現)
#include "stdafx.h" #includeusing namespace std; int main() { int sum=0,value; while(cin>>value) sum+=value; cout< 關於輸入結束,詳見讀入未知數目的輸入(輸入非整數結束)
變量和基本類型
1.基本字符集(char)
擴展字符集(wchar_t)
2.整型(integral type):表示整數、字符和布爾值的算術類型的合稱
3.字面值規則
20 0240X14
20L 20UL 20FL(F單精度)
科學計數法
字符串字面值:為了兼容C語言,C++中所有的字符串字面值都由編譯器自動在末尾添加一個空字符。(類型:const char類型的數組)
4.轉義字符 \***
5.初始化(創建變量並賦值) 賦值(擦除對象的當前值並用新值代替)
int a = 10;copy-initialization 更靈活且效率更高
int a(10);direct-initialization
6.構造函數(constructor)定義如何進行初始化的成員函數。(可以有多個)
PS:(P43第二小節末一句有點小瑕疵,可以這樣理解)有多個初始化參數時不能使用復制初始化。
7.定義(definition)用於為變量分配存儲空間,還可以為變量指定初始值。
聲明(declaration)用於向程序表明變量的類型和名字 extern
int i; //declares and defines i extern int i; //declares but does not define i extern int i = 1; //definition8.非const變量默認為extern。const變量默認時是定義該變量的文件的局部變量。
int i; //全局變量 const int i = 1; //文件的局部變量 extern const int i = 1; //const定義時必須初始化(const變量默認時是定義該變量的文件的局部變量。)why?允許const變量定義在頭文件中。見13
const變量定義時必須初始化!(利用定義只有一次,達到不能修改的目的)
可以用const對象初始化非const對象,反之亦然。因為初始化復制了初始化式的值。
9.引用(reference)就是它綁定對象的另一個名字。引用返回左值。主要用作函數的形式參數。
a reference must be initialized, and initializer must be an object.
當引用初始化後,只要引用存在就保持綁定到初始化時指向的對象。不可能將引用綁定到另一個對象。
復合類型(compound type 用其它類型定義的類型)(注:不能定義引用類型的引用)
10.typedef可以用來定義類型的同義詞
typedef的目的之一:一種類型用於多個目的時,使得每次使用該類型的目的明確。
PS :本書部分翻譯確實不太准確,雖然我還沒看原版,但是感覺是醬紫的。。
11.枚舉enumeration:解決數值的相關聯問題。
枚舉成員值可以是不唯一的。
每個enum都定義一種唯一的類型。枚舉類型的對象的初始化或賦值,只能通過其枚舉成員或同一枚舉類型的其它對象來進行!
12.class和struct關鍵字定義類的唯一差別在於默認訪問級別:默認情況下,struct的成員為public;class的成員為private。
13.頭文件(header file)不應該含有定義!
三個例外:類、(值在編譯時就已知道的)const對象、inline函數。
原因:編譯器需要知道他們的定義來生成代碼。
const對象定義在頭文件中時,必須是用常量表達式初始化的const對象!ps:後續章節-const對象定義在頭文件中(待完善)
14.#include設施是C++預處理器(preprocessor)的一部分。
#include指示只接受一個參數:頭文件名。預處理器用指定的頭文件的內容替代每個#include。
15.頭文件保護符(header guard),用於避免在已經見到頭文件的情況下重新處理該頭文件的內容。(實際就是防止多重定義)
預處理器指示 #ifndef #define #endif預處理器變量:有兩種狀態(已定義和未定義)。不能用預處理器變量給自定義的變量命名(如 a=NULL //error)。
標准庫類型
1.抽象數據類型ADT(abstract data type)
2.頭文件中應該只定義確實必要的東西。
使用完全限定的標准庫名字,即不要using聲明。(因為包含頭文件的程序也會有此using聲明,不管其是否需要)
3.字符串字面值與標准庫string類型不是同一種類型。
4.讀入未知數目的string對象
string word; //read until end-of-file, writing each word to a new line while(cin >> word){ cout <5.讀入未知數目的行
6.不要把string的size操作的返回值賦值給一個int變量!(其返回值為:string::size_type類型,不返回int類型的目的是machine-independent)
保存一個string對象size的最安全的方法就是使用標准庫類型string::size_type。
string對象的索引變量最好也用string::size_type類型。
7.標准庫類型盡量設計得和基本數據類型一樣方便易用。
8.string庫類型的賦值操作:釋放原內存,分配新內存,復制。
9.C標准庫頭文件命名形式為name.h,而C++版本的此頭文件的命名形式為cname(c表示這個頭文件源自C標准庫)。例cctype和ctype.h
通常,C++程序應該采用cname這種頭文件的版本。
10.vector對象動態增長
11.vector對象的size,返回相應vector類定義的size_type的值。
vector12.向vector添加元素::size_type //ok vector::size_type //error string word; vector13.C++程序員習慣於優先選用!=而不是<來編寫循環判斷條件。(泛型編程)text; while(cin>>word){ text.push_back(word); } 14.迭代器(iterator)是一種檢查容器內元素並遍歷元素的數據類型。
現代C++程序更傾向於使用迭代器而不是下標操作訪問容器元素。迭代器(iterator)和迭代器類型(例vector
::iterator) 由end操作返回的迭代器指向vector的“末端元素的下一個”(off-the-end iterator)。只是起一個哨兵(sentinel)的作用,表示我們已處理完vector中所有元素。
解引用操作符(*操作符)*iter = 0;
const_iterator
任何改變vector長度的操作都會使已存在的迭代器失效。
15.用string對象初始化bitset對象(從string對象讀入位集的順序是從右向左!)
數組和指針
1.設計良好的程序只有在強調速度時才在類實現的內部使用數組和指針。
2.數組缺點:長度固定;無size操作;不允許直接復制和賦值
但是還是要會~~
3.數組下標的正確類型:size_t
4.緩沖區溢出(buffer overflow)
5.理解指針聲明語句時,請從右向左閱讀。
6.如果必須分開定義指針和其所指向的對象,則將指針初始化為0.(NULL等效於0,是繼承自C的預處理變量,位於cstdlib頭文件中)
7.void*指針表面該指針與一地址相關,但不清楚存儲在此地址上的對象的類型。只支持有限的操作:
與另一個指針進行比較;
向函數傳遞void*指針或從函數返回void*指針;
給另一個void*指針賦值。
8.指針和引用的比較
引用總是指向某個對象:定義引用時必須初始化(引用一經初始化,就始終指向同一個特定對象)。
賦值行為的差異:引用賦值修改的是該引用所關聯的對象的值。
9.兩個指針減法操作的結果是標准庫類型ptrdiff_t的數據。在cstddef頭文件中定義。
10.只要指針指向數組,就可以對它進行下標操作。
int *P = &ia[2]; int j = p[0]; int k = p[-2];11.指向const對象的指針(理解為“自以為指向const的指針”),常用作函數的形參(以此來確保傳遞給函數的實際對象在函數中不因為形參而被修改)
可以將指向const對象的指針初始化為指向非const對象,但不可以讓指向非const對象的指針指向const對象。
12.閱讀const聲明語句產生的部分問題,源於const限定符既可以放在類型前也可以放在類型後
例:指針和typedef
typedef string *pstring; //all three decreations are the same type.they are all const pointers to string. const pstring cstr1; string *const cstr2; pstring const cstr3;13.C風格字符串(C-style character string)不能確切的歸結為C語言的類型,也不能歸結為C++語言的類型,而是以空字符null結束的字符數組。
C++通過(const)char*類型的指針來操縱C風格字符串。(如果沒有null結尾...)const char *cp = "some value"; while(*cp){ //do something to *cp cp++; }永遠不要忘記字符串結束符null14.自由存儲區(free store)或堆(heap):每個程序在執行時都占用一塊可用的內存空間,用於存放動態分配的對象。
C語言:malloc和free
C++語言:new和delete
15.動態數組
使用跟在數組長度後面的一對空圓括號,對數組元素做初始化。
int *pia = new int[10]; //array of 10 uninitialized ints int *pia2 = new int[10](); //array of 10 int value-initialized to 0調用new創建長度為0的數組是合法的(但不能被解引用)char arr[0]; //error:can not define zero-length arrary char *cp = new char[0]; //ok:but cp can not be dereferenced動態空間的釋放(內存洩露memory leak)delete[]
16.可以使用C風格字符串對string對象進行初始化或賦值。
需要借助c_str()來實現string對象對C風格字符串的初始化或賦值。
const char *str = st2.c_str();17.指向多維數組的指針int *ip[4];//array of pointers to int int (*p)[4];//pointer to an array of 4 ints用typedef簡化指向多維數組的指針
表達式
1.C++提供了豐富的操作符,並定義操作數為內置類型時,這些操作符的含義。(操作符operator的含義,取決於操作數operand的類型)
unary operator、binary operator、ternary operator
要理解由多個操作符組成的表達式,必須先理解操作符的優先級(precedence)、結合性(associativity)和操作數的求值順序(order of evaluation)。
precedence:例 低優先級的應用
int i; while((i = get_value()) != 42){ //get_value() returns an int //do something }associativity:(例:左結合cin>>a>>b 右結合int a=b=0)
order of evaluation:C++中規定了操作數計算順序的操作符有&&、||、逗號運算符、條件運算符。例見3 short-circuit evaluation
2.操作符%(remainder / modulus)兩個操作數為正正、負負、正負、負正時的結果。(後兩種取決於機器)
3.短路求值(short-circuit evaluation):邏輯與和邏輯或操作符總是先計算其左操作數,然後再計算其右操作數。只有在僅靠左操作數的值無法確定該表達式的結果時,才會求解其右操作數。
4.對於位操作符,由於系統不能確保如何處理其操作數的符號位,所以強烈建議使用unsigned整型操作數。
<<補零,>>依據具體
5.復合賦值操作符(左操作數只計算了一次,而使用相似的長表達式時則計算了兩次)
6.只有在必要時才使用後置操作符。(因為前置操作需要做的工作更少)
7.箭頭操作符(->)C++為在點操作符後使用的解引用操作定義的同義詞。
8.sizeof的一點點說明,注意理解。
將 sizeof 應用在表達式 expr 上,將獲得該表達式的結果的類型長度,但是卻沒有計算表達式 expr 的值!
(特別是在 sizeof *p 中,指針 p 可以持有一個無效地址,因為不需要對 p 做解引用操作。)
9.逗號操作符:結果是其最右邊表達式的值;常用於for循環。
10.類型轉換:隱式類型轉換(implicit type conversion)、算術轉換(arithmetic conversion )、顯式轉換(強制類型轉換cast)
1)何時發生隱式類型轉換
混合類型的表達式、條件表達式被轉換為bool類型、用表達式初始化(或賦值)變量時、函數調用。
2)強制類型轉換(static_cast、dynamic_cast、const_cast、reinterpret_cast)(盡量避免使用)
舊式強制類型轉換
type (expr);//Function-style cast notation (type) expr;//C-language-style cast notation
語句
1.使用空語句時應該加上注釋,以便任何讀這段代碼的人都知道該語句是有意省略的。
2.復合語句(compound statement),通常被稱為塊(block),是用一對花括號括起來的語句序列。
復合語句用在語法規則要求使用單個語句但程序邏輯卻需要不止一個語句的地方。(例:循環語句的循環體)->復合語句在語法上是單個語句
3.一個類類型能否用在條件表達式中取決於類本身。(IO類型可以)
4.switch語句提供了一種更方便的方法來實現深層嵌套的if/else邏輯。
case label的執行流:從該點開始執行,並跨越case邊界繼續執行其他語句,直到switch結束或遇到break語句為止。(故意省略case後面的break語句時,應提供一些注釋說明)
default label提供了相當於else子句的功能。(定義default標號是為了告訴它的讀者,表面這種情況已經考慮到了)
為了避免出現代碼跳過變量的定義和初始化的情況,對於switch結構,只能在它的最後一個case標號或default標號後面定義(作用域為整個switch的)變量。塊語句例外。
5.do while語句總是以分號結束。
6.break語句用於結束最近的while、do while、for或switch語句,並將程序的執行權傳遞給緊接在被終止語句之後的語句。
7.continue語句導致最近的循環語句的當次迭代提前結束。只能出現在for、while或do while循環中。
8.throw表達式、try塊(try block)、catch子句(catch clause)/處理代碼(handler)、異常類(exception class)
尋找處理代碼的過程與函數調用鏈剛好相反。(balabala)
9.使用預處理器進行調試
思想:程序所包含的調試代碼僅在開發過程中執行。當應用程序已經完成,並且准備提交時,就會將調試代碼關閉。
int main(){ #ifndef NDEBUG cerr<< "starting main"<使用NDEBUG預處理變量以及assert預處理宏進行調試。 與異常(異常用於處理程序執行時預期要發生的錯誤)不同,程序員使用assert來測試“不可能發生”的條件。
函數形參(parameter)、調用操作符(call operator即一對圓括號)、實參(argument)
1.函數調用(做了兩件事情):用對應的實參初始化函數的形參;並將控制權轉移給被調用函數。主調函數(calling function)的執行被掛起,被調函數(called function)開始執行。
2.在C語言中,具有const形參或非const形參的函數並無區別。C++為了兼容C,所以、、、
3.復制實參的局限性。解決辦法:將形參定義為引用或指針類型。(從C語言背景轉到C++的程序員習慣通過傳遞指針來實現對實參的訪問。在C++中,使用引用形參則更安全和更自然)
4.非const形參:避免復制實參;且使用引用形參返回額外的信息(也可通過額外的引用參數返回)。
const形參:避免復制實參,且防止修改相應實參。
應該將不需要修改的引用形參定義為const引用。普通的非const引用形參在使用時不太靈活:既不能用const對象初始化,也不能用字面值或產生右值的表達式實參初始化。
5.傳遞指向指針的引用
void ptrswap(int *&v1, int *&v2){ //int *&v1的定義應從右至左理解:v1是一個引用,與指向int型對象的指針相關聯。 int *tmp = v2; v2 = v1; vi = tmp; }交換指向對象的指針,來代替,交換指針指向的對象~(提高性能?只是某些時候吧?)
6.C++更傾向於通過傳遞指向容器中需要處理的元素的迭代器來傳遞容器。7.數組形參,編譯器忽略任何數組形參指定的長度。
通過引用傳遞數組,數組大小成為形參和實參類型的一部分,編譯器檢查數組實參的大小與形參的大小是否匹配。
f(int (&arr)[10]) //arr is a reference to an array of 10 ints f(int &arr[10]) //arr is an array of references8.多維數組的傳遞:編譯器忽略第一維的長度。void printValues(int matrix[][10], int rowSize)9.任何處理數組的程序都要確保程序停留在數組的邊界內。有三種常見的編程技巧:1)在數組本身放置一個標記來檢測數組的結束。(例如,C風格字符串)
2)傳遞指向數組第一個和最後一個元素的下一個位置的指針。(標准庫中的技術)
3)將第二個形參定義為表示數組的大小。這種用法在C程序和標准化之前的C++程序中十分普遍。
10.省略符形參:暫停了類型檢查機制。
11.return語句
沒有返回值的函數:return;(或者隱式的)
具有返回值的函數:return expression;
在含有return語句的循環後沒有提供return語句是很危險的,因為大部分的編譯器不能檢測出這個漏洞。
千萬不要返回局部對象的引用或指針!12.函數原型(function prototype)=函數返回值+函數名+形參列表
13.默認實參:既可以在函數聲明也可以在函數定義中指定默認實參。但是在一個文件中,只能指定一次默認實參。
通常,應在函數聲明中指定默認實參,並將該聲明放在合適的頭文件中。(另:只有文件中包含默認實參時,默認實參才是有效的)
14.自動對象(automatic object)、static局部對象(static local object)
size_t count_calls(){ static size_t ctr = 0; return ++ctr; }15.內聯函數避免函數調用的開銷(調用函數要比求解等價表達式要慢得多)。將函數指定為內聯函數,就是講它在程序中每個調用點上“內聯地”展開。
內聯機制適用於優化小的、只有幾行的而且經常被調用的函數。
內聯函數應該在頭文件中定義!
16.類的成員函數:this指針、常量成員函數(const member function)、構造函數(constructor)
this指針,每個成員函數(除了static成員函數外)都有一個額外的、隱含的形參this。
常量成員函數(const member function),const this指針
構造函數的初始化列表(constructor initializer list)
合成的默認構造函數(synthesized default constructor),不會自動初始化內置類型的成員!一般適用於僅包含類類型成員的類。
17.重載函數(overloaded function):出現在相同作用域中、具有相同的名字、形參表不同
函數重載(function overloading)與重復聲明(redeclaration)的區別
1)重載與作用域
一般來說,局部地聲明函數是一種不明智的選擇。(會屏蔽而不是重載在外層作用域中聲明的同名函數)
在C++中,名字查找發生在類型檢查之前。
2)函數重載確定(overload resolution,即函數匹配function matching)是將函數調用與重載函數集合中的一個函數相關聯的過程。
3)函數重載確定的過程:
第一步,確定該調用所考慮的重載函數集合。該集合中的函數稱為候選函數(candidate function),它是與被調函數同名的函數,並且在調用點上它的聲明可見。
第二步,選擇可行函數(viable function):從候選函數中選擇一個或多個函數,它們能用該調用中指定的實參來調用。
第三步,尋找最佳匹配
4)實參類型轉換
精確匹配(exact match)
通過類型提升(promotion)實現的匹配
通過標准轉換(standard conversion)實現的匹配
通過類類型轉換(class-type conversion)實現的匹配
5)可以基於函數的引用或指針形參是指向const對象還是指向非const對象,實現函數重載。(不能基於指針本身是否為const來實現函數的重載)
僅當形參是引用或指針時,形參是否為const才有影響
18.指向函數的指針
bool (*pf)(const string &, const string &);//pf points to function bool *pf(const string &, const string &); //pf is a function that returns a bool*1)用typedef簡化函數指針的定義(見下例)2)可用函數名對函數指針做初始化或賦值(見下例)。(直接引用函數名等效於在函數名上應用取地址操作符)
3)指向函數的指針,可以不需要使用解引用操作符直接通過指針調用函數:
typedef bool (*cmpFcn)(const string &, const string &); cmpFcn pf = lengthCompare; //lengthCompare is the name of a functionlengthCompare("hi","bye"); //direct callpf("hi","bye");
(*pf)("hi","bye"); 4)函數指針的形參5)返回指向函數的指針(理解的最佳方法:從聲明的名字開始由裡而外理解)
6)指向重載函數的指針
ps:4、5、6待補充。
標准IO庫
1.IO類型通過繼承關聯,所以可以只編寫一個函數,而將它應用到三種類型的流上:控制台、磁盤文件、字符串流。
2.出於某些原因,標准庫類型不允許做復制和賦值操作(闡述類和繼承時說明原因)。有兩層含義:
1)不存在存儲流對象的指針或引用;(只有支持分支的元素類型可以存儲在vector或其他容器類型裡)
2)形參和返回類型也不能為流類型。(傳遞和返回IO對象時,需要用引用和指針)