1. 程式人生 > >C++ Primer 讀書筆記

C++ Primer 讀書筆記

1,命令編譯生成的預設輸出檔案(可執行檔案) 命名為:a.out(Unix), a.exe(Windows)
2,cout輸出首先會存到快取中,而printf之類的輸出會直接輸出到輸出流中。
3,可以從鍵盤上輸入End-Of-File:Ctrl+d(Unix), Ctrl+z(Windows)。
4,C++中最常見的三種編譯錯誤:1)型別錯誤,將值賦給不同型別的變數;2)宣告錯誤,使用未宣告的變數,或者在同一個域中重複宣告一個變數;3)語法錯誤,寫錯語法。
5,可以通過command <infile >outfile實現輸入輸出重定向。
6,C++中,內建型別的大小(sizeof)因機器不同可能會有所差異,但是相對大小是確定的,比如int的大小不會小於short的大小。sizeof所求的大小以位元組為單位。
7,C++中字元的幾種型別:
     char最基本的字元型別,8bits,可以儲存一些基本的字元。      wchar_t,16bits,可以儲存計算機最大的擴充套件字符集。      char16_t, 16bits,用於儲存Unicode編碼字元。
     char32_t, 32bits,同上。
8,char也分signed和unsigned。
9,浮點轉整形,並不是通過向下或者向上取整,而是無論正負,直接將浮點部分捨去,只保留整數部分。
10,如果試著將一個超出範圍的值賦給一個有符號型別的變數,將會導致這個變數undefined,不能直接使用。
11, 有的時候為了節省記憶體,可以用char儲存整數並進行運算,但是char在有的機器預設是有符號的,而另一些機器是預設無符號的,所以使用之前最好指定是 否是有符號型別;但是,不要用bool儲存整數和進行算數運算,絕大部分機器,會將賦值給bool型別變數的整數轉換為0和1,所以用bool儲存整數和 進行算數運算幾乎可以確定會出錯。
12, 用轉義方式表示字元:\x...十六進位制,\...八進位制。
13,在C++中,初始化和賦值是有區別的,初始化是變數被建立時的第一次賦值(如果沒有顯示初始化則會被初始化為預設的值)。比如子類可以給從父類繼承的變數直接賦值,但是不能直接對它初始化。
14, 變數的初始化:可以通過int a = v; int a(v); int a{v}; int a = {v};前兩者較為常見,後兩者是新標準,成為list初始化,這種初始化,當遇到可能丟失資訊的初始化時,會報編譯錯誤。list初始化可以用來初始化 vector。
15,C++中變數,函式內定義的變數預設初始化值是undefined,而函式外定義的變數預設初始化值是0.
16, 宣告和定義,定義是一種宣告形式,但是除了初始化它還會給變數申請一塊記憶體,並且可能給其賦值。一個變數可以被宣告多次,但只能被定義一次。通常用 extern來宣告變數,如果用了extern的同時還給變數賦值,就會把extern的效果覆蓋,變成了定義(extern int a = 0;);並且,如果在函式內用了extern宣告,就不能同時對變數賦值(比如這種形式extern int a = 0;),否則會報編譯錯誤。
17,引用必須被初始化,並且只能和一個變數繫結,一旦綁定了就不能在和其他變數繫結。比如int &a = b; 而int &a;和int &a = 0;就不對。
18,與引用不同,指標可以指向多個不同的變數,儲存變數的所在地址。
19, 引用不是變數,所以指標不能指向引用。但是引用可以引用指標,比如int *p; int *&r = p;這個例子中,p是一個整型指標,r是指標p的一個引用。遇到引用和操作符多個連用的情況,可以從右到左逐個看,最右邊的就是變數的真正型別。比如上述 例子中,r是個引用,他所引用的型別是一個指標p,p是指標,它所指向的內容是一個整數。
20,const 常量:const常量在編譯時就會被初始化,每次引用const常量在編譯期間都會被替換成常量值。但是,const常量是Local to file的,一般情況下只能在被定義的檔案中使用。正因為const常量是Local to file的,在不同檔案定義相同名字的常量會被視為在這些檔案裡面分別定義,不會衝突,所以可以在多個檔案定義同一個常量,但是這樣會導致程式碼冗餘,如果 一個常量需要修改就會導致多個檔案的修改,並且如果該常量的初始化部分不是常數表示式,會導致多次非常數時間的計算,可能會影響效率。一個正確的方法是: 定義一次,宣告多次。如果一個const變數要在多個檔案使用,在定義時必須加extern。比如在file.cpp裡面定義extern const int a = f(); 在file.h可以這樣宣告:extern const int a;這樣就可以正常使用了。
21,const引用,const引用可以“繫結”非初始化變數,限制比非const引用更少:
比 如,double d = 1.2; int &a = d;這條語句會報錯,原因是非const引用只能引用同種型別的變數。而const引用卻不是這樣的,比如const int &ca = d; 甚至const int &ca  = d*2;都是合法的。原因在於,對const引用初始化時,編譯器自動生成一箇中間變數,比如const int &ca = d;會被編譯器自動轉成以下程式碼:double d = 1.2; const int tmp = d; const int &ca = tmp; tmp是一個臨時變數,系統生成的,沒有名字,因此程式設計師不能夠通過名字去訪問。
22,Top- Level const 和 Low-Level const:通常出現在定義指標中,Top-Level描述指標本身的,Low-Level是描述指標所指向的值。比如const int * a = NULL;中的const是Low-Level的,他標識該指標所指向的值不能被該指標修改。int *const a = NULL;中的const是Top-Level的,他表示該指標本身不能被修改。引用是沒有Top-Level const的,因為他本身不是物件。在拷貝的過程中會忽略Top-Level const。
23,constexpr int a = f();標識a的初始化值必須是一個常量表達式,並且一旦初始化以後就不能再改變。
24,auto i  = a;會根據i的初始化值的型別自動確定i的型別。decltype(a) b = 0; 會根據a的型別來定義b。
     兩者的區別:auto會推斷目標變數(引用或者指向)的型別,decltype則會推斷目標變數的型別。比如int &r = i; auto a = r;此時的a是int型別的;而int &r = i; decltype(r) i = a; r卻是int&型別的。auto會忽略Top-Level const,而decltype則不會忽略。decltype((i))得到的結果是i對應型別的引用。
25,using a = int; C++11中定義型別的方式。
26,一般來說,標頭檔案裡面不要用using namespace ...;因為引入標頭檔案的地方會標頭檔案copy一份到本檔案,所有引用該標頭檔案的地方都會using 這個namespace,可能會導致命名衝突。
27,為了相容C,C++中的字串常量和string是不一樣的,並且在字串+運算中,必須有一個運算數為string的。
28,vector可以用list初始化,也可以用自帶的初始化函式:(n, s);n是初始化size,s是初始化值(可選)。
29,陣列之間不可賦值。 30,沒有引用陣列,只有對陣列的引用。int &refs[10] = ...;是錯誤的;而int (&arrRef) [10] = ...;是對的。 31,int *ptrs[10];是由10個指標組成的陣列;而int (*Parray)[10];則是指向對由10個元素組成的陣列的指標。 32,字元陣列切記要有空結束符'\0'。 33,*p實際上是對指標p指向物件的引用。 34,一般的操作符並不能保證運算元的evaluate的順序,比如int a = fa()+fb();就不能確定是哪個函式先執行。但是有四種操作符能夠確保運算元的evaluate的順序,分別是:&&, ||, ? :, ,(逗號)。
35,在新的版本中,整數除法如果有餘數,結果無論正負都向靠近0的方向舍入(truncated)。對於帶有負號的除法或者取模運算,如果是除法,將負號提出來。如果是取模,分情況討論,m%(-n) = m%n,(-m)%n = -(m%n);
36,取引用的優先順序低於後自增,比如 *ptr++ = *(ptr++);, 也低於成員訪問符,比如*p.size()是錯誤的,應該是(*p).size();
37,這種形式的用法要注意,*ptr = f(*ptr++);這條語句是undefined的。
38,型別轉換的基本規則:小轉大,有符號轉無符號。
39,switch 的分支值必須是常數表示式。switch的每個分支必須要加break。不然後面的條件包括該條件下的其他語句也會執行,即使是最後一個分支,都最好加 break;每個switch最好加上default,即使default情況下不做什麼處理,也最好加上,這樣做可以表明寫程式時已經考慮到這種情況 了。case語句中:不能給變數初始化,除非case語句加上{}。
40,C++中如果異常沒有被捕獲,就會自動呼叫系統程式terminate終止程式。
41,C++中函式不能返回陣列和函式,但是可以返回他們的指標。 42,傳二維陣列需要指定第二維的大小,儘量用這種:void f(int (*matrix)[10], int rowSize) {}, 而儘量不要用void (f(int matrix[][10], int rowSize) {}。 43,int main(int argc, char *argv[]) {},argc是使用者輸入引數的個數,而argv的大小是argc+1個,其中argv[0]是程式名稱,後面argc個才是使用者輸入的引數。 44,函式可變數量的引數,可以用新版本的initializer_list<T> l;傳遞,也可以用省略號(...)傳遞,如果不是考慮相容C,應該選擇用initializer_list傳遞。 45,C++中返回陣列指標的幾種方式:      1) typedef int arrT[10]; / using arrT = int[10];           arrT* func(int i);      2) int (*func(par_list)) [dimension];      3) auto func(int i) -> int(*) [10];      4) decltype(arr) * arrPtr(int i); 46,最好不要在區域性域內宣告函式,否則會將外部的同名函式都hide起來。
47,函式預設引數必須從左到右順序宣告,也就是說,第n個引數聲明瞭預設值,那麼第n+1個引數必須宣告預設值。並且預設引數的預設值不一定要是常數或者常量表達式。
48,inline(內聯)函式被呼叫時,不需要像一般函式呼叫那樣(儲存暫存器資訊,跳到新的地方執行,返回病恢復暫存器資訊),而是直接講函式內部的程式碼複製到調用出去執行。並且inline函式必須在呼叫它的每個檔案裡面定義,對於同一個程式的不同檔案,如果inline函數出現的話,其定義必須相同。是否支援遞迴inline取決於編譯器。inline函式最好定義在標頭檔案裡面。

49,constexpr函式要求返回型別和引數型別都為常數表示式,但是對於具體的值可以不是常數表示式。
50,可以在檔案開頭處新增:#define NDEBUG 表示不需要除錯,所以檔案中的所有除錯語句(assert)都不會執行。
51,函式返回函式指標的方法:
     1) using F = int(int*, int); F* f1(int); 或者 using PF = int(*) (int*, int); PF f1(int);
     2)  int (*f1(int)) (int*, int);
     3) auto f1(int) -> int (*) (int*, int);     (trailing return)
52,定義在類中的函式隱式地為inline函式。
53,物件成員函式的const載入方法引數列表後面,表示物件的this是一個指向const變數的const指標,指標本身和指標指向的值都不能修改。並且const物件只能訪問物件的const方法。
54, 預設建構函式:如果一個類沒有定義建構函式,編譯器會自動生成一個預設的(無引數)建構函式。如果類添加了新的建構函式,又想繼續保留預設建構函式,可以 自己定義,或者在新標準下還可以ClassName() = default;來定義預設建構函式,這標識這個預設建構函式使用系統自動生成的。
55,還可以對成員變數宣告mutable,比如mutable int a;這表示變數a是可變的,即使在const函式裡面,也是可以修改。
56,成員函式的定義在編譯器處理所有類的宣告之後處理。
57,類中,變數宣告,首先看成員函式內部變數的宣告,檢視類成員變數宣告,成員函式定義前的作用域。
58,類的成員變數的初始化順序和成員變數在類中的定義順序一致。 59,所有引數都有預設值的建構函式也可以用作預設建構函式。在C++11中還支援delegating 建構函式,比如ClassName(): ClassName(0, "", 1) {}
60,用預設建構函式定義變數應該是ClassName obj; 而不是ClassName obj(); 後者是宣告一個返回ClassName物件的函式。
61,類或結構可以使用list initializer,但是初始化順序必須和成員變數定義的順序一致,並且所有成員變數必須是public的。
62, 類的建構函式沒有const型別,但是可以在類的建構函式宣告前面加constexpr,如果在普通函式宣告前加constexpr,則說明這個函式只能 有return語句,但是建構函式沒有返回型別,就是說它不能返回,所以加了constexpr的建構函式的函式體為空,成員變數的初始化只能用變數自帶 初始化器,比如constexpr ClassName(int a): a(1) {};  也可以定義為default構造器,使用自動生成的預設構造器。
63,類的靜態變數,可以用於類成員函式的預設引數,還可以在類裡面定義與該類同型別的變數,比如                 ClassName {     public: static ClassName A; //但是ClassName A;卻是錯誤的。
     }
64,cout 輸出首先會儲存到快取裡面,到一定的時間會講快取中的資料flush從而列印到目標輸出地址,這可能會引發一個異常情況,如果程式突然crash掉,但是 cout的快取還有沒有flush的內容,那麼在目標輸出地址就看不到這些內容。為了避免這種情況,可以通過一些方式強制將cout快取中的內容 flush出來:
     1) cout << flush;
     2)cout << endl;
     3) cin >> ... ;  //cin和cout是tie關係,當cin出現的時候,cout的快取會立即flush。可以通過設定tie關             系改變這類效果。
     流物件不能賦值,不能傳引數,也不能當做返回型別,但是可以傳遞或者返回他們的引用。 65,可以通過sstream標頭檔案裡面的stingstream來讀寫string裡面的內容:      1)讀s中的內容,istringstream = s; istringstream >> a;          2)寫s中的內容,ostringstream = s; ostringstream << a;
     ostream和instream不能被深拷貝。 66,迭代器支援算數運算的容器包括:vector, string,deque, array。
67, 除了array以外的其他容器,都有自己的預設構造器,並且預設初始化為空;並且除了array以外的順序容器,他們還支援指定size和初始值的初始 化,非順序容器並不支援指定size初始化。為了避免不同size array之間的賦值,array不支援大括號list賦值。比如array<int, 10> A = {0, 1, 2, 3}; A = {1, 2}; 第二句是不對的。
68,容器可以通過拷貝其他容 器來初始化,如果直接通過A來對容器B初始化,則需要兩個容器的型別以及容器內包含的元素都相同;比如vector<int> A = {1, 2, 3}; vector<int> B(A); 但是如果通過迭代器的方式來初始化,限制則沒那麼嚴格,比如list<const char*> A = {"ab", "ba"}; vector<string> B(articles.begin(), articles.end());,不需要容器型別相同,只需要容器內元素型別之間可以轉換。類似地,容器可以通過呼叫assign來賦值,比如 list<int> A; list<int> B; B.assign(A);這種方法A和B的型別以及他們內部元素的型別都要相同;或者list<int> A; list<int> B; B.assign(A.begin(), A.end());這種方法不需要A和B的型別相同,只需要保證A中的元素型別可以想B中的元素型別轉換,務必確保assign裡面沒有自身的迭代器。還 可以list<string> A; A.assign(10, "yes");
69,array的引數模板是<type, size>,比如array<int, 42> A; 而 array<int> 就是錯誤的。
70, 相同容器,並且容器包含的元素型別相同,那麼兩個容器可以相互比較,具體實現大概是逐個比較容器內的元素大小來確定大小關係,有序容器(除了 unordered_map和unordered_set等以外的容器)則會按照元素順序來進行對比。當然,要確保容器中的元素支援比較操作。
71,除了array以外的順序容器,支援insert操作:
     1) c.insert(p, t);在p元素前面插入一個元素t,返回指向元素t的迭代器,p也是一個迭代器。
     2)c.insert(p, n, t); 在p元素前面插入n個職位t的元素,返回這n個元素的第一個元素。
     3)c.insert(p, b, e);在p元素前面插入一段元素,這一段元素有兩個迭代器b和e指定,b和e都不能是c中的迭代器。
     4)c.insert(p, {1, 2, 3});在p元素前面插入一段有大括號list組成的元素,返回插入元素的第一個元素所對應的迭代器。
     如果沒有插入元素,則返回迭代器p。 72,list和forword_list都是從前面(front)插入元素。
73, 在C++11中,為容器新添加了一種操作emplace,可以在容器指定位置位置插入一個新的元素,傳參的型別和容器內部元素型別的建構函式的形參一致。 emplace後,容器會為新元素分配一塊空間,並將新元素插入到指定位置。而一般的插入,是新建一個區域性臨時變數,然後將這個元素插入到容器中。通常的 插入主要有三種emplace操作:
     1)c.emplace_back(args); 效果類似c.push_back。
     2)c.emplace_front(args);效果類似c.push_front。      3)c.emplace(args);效果類似insert。      只是類似,但記憶體分配上有差異。 74, 對vector或者string的迭代器,指標以及引用,一旦容器儲存空間被重新分配,他們就會失效。如果儲存空間沒有被重新分配,但是有新的元素插入, 那麼插入元素前的迭代器(或者指標,引用)保持有效,但是插入元素後面的迭代器(或者指標,引用)會失效。其他容器也會有類似的情況,所以在使用對容器的 迭代器,指標和引用的時候要考慮他們是否會失效。
75,對於線性容器,有三個和容器容量有關的函式:
     1)shrink_to_fit():請求容器釋放空間,使得容器的空間剛好夠儲存已有元素。
     2) capacity():計算容器的容量,不是已有元素的個數。
     3) reserve(n):為容器預分配n個元素的空間,不會影響容器已有元素的個數,reserve的空間小於當前容器的空間,reserve失效。
76,Lambda 表示式:lambda表示式是一個callable物件,當一個lambda表示式被定義的時候,編譯器會它自動生成一個匿名類,當lambda表示式被 傳入一個函式的時候,編譯器會自動定義一個lambda表示式的類物件。它可以用作實現簡單,並且使用次數和地方少的情況。表示式格式如下:
     [capture list] (parameter list) -> return type { function body }
     capture list是一些lambda表示式函式的區域性變數,通常為空,但是不能省略。
     lambda必須通過trailing return方式來指定返回型別。
     在lambda表示式中,引數列表和返回型別可以省略,但是capture list和函式體不能省略。
     比如 auto f = [] { return 42; };     cout << f() << endl;
     如果lambda表示式有引數列表,可以這樣寫:
     [] (const int &a, const int &b) {return a < b; }
     lambda表示式也可以當做一個函式的引數使用,比如可以用lambda表示式實現sort函式的比較函式:      sort(nums.begin(), nums.end(), [] (const int &a, const int &b) { return a < b; });
     capture list 用來只用來宣告非靜態區域性變數,比如: find_if(nums.begin(), nums.end(), [threshold] (const int & a) {return a < threshold;}); capture list 可以capture一個變數的引用,但是要確保在使用這個變數的引用時,這個變數存在。capture list有以下幾個用法:
     1)[], 空capture list。
     2)[names],capture多個變數,以逗號分隔。
     3)[&],隱式地將lambda表示式函式體所使用的變數的引用capture。      4)[=],隱式地將lambda表示式函式體所使用的變數capture。
     5)[&, identifier_list],隱式地將lambda表示式函式體所使用的變數的引用capture,除了identifier_list包含的變數([&, a, b]),lambda表示式直接使用這些變數本身。
     6)[=, reference_list],隱式地將lambda表示式函式體所使用的變數的引用capture,除了reference_list包含的變數,這 些變數以引用的方式列出來([=, &a, &b]),並且以引用的方式被使用。
     lambda表示式可以直接使用區域性靜態變數,不需要在capture list裡面宣告。
     lambda表示式預設是不能改變capture list的元素,但可以在函式體前面宣告mutable,表示函式體可以改變capture list的元素。
77,bind函式:bind是functional標頭檔案裡面的一個函式,用於繫結函式與引數。表示式形如:
     auto newCallable = bind(callable, arg_list);
     bind的第一個引數是原函式本身,arg_list的形式也和callable一致,但是可以為arg_list繫結具體的值或者placeholders。比如:
     auto f1 = bind(f0, a, b, std::placeholders::_2,std::placeholders:: _1);
     此時如果要呼叫f1,需要傳入兩個引數,並且當f1被呼叫時,就相當於呼叫f0,並且將a當做f0的第一個引數,b當做第二個引數,f1的第二個引數當做f0的第三個引數,f1的第一個引數當做f0的第四個引數,傳入f0。
78,在C++11中,map和set也可以用list initialization,比如:
     set<string> S = {"the", "but", "and"};
     map<string, string> M = { {"1", "one"}, {"2", "two"}, {"3", "three"}};
79,Associative Container的insert(或者emplace)函式:用於向容器中插入元素,返回值是一個pair,pair的第一個引數是給定key對應的迭代器,pair的第二個引數是一個bool型別的引數,表示是否插入成功。
80,multiset 和multimap:與set和map類似,但是在multiset和multimap中,一個key可以對應多個value。查詢的時候,會將給定 key值查到的第一個元素的迭代器返回。如果想知道給定key值在multi容器中有幾個對應的元素,可以使用c.count(key)方法。如果想遍歷 multi容器中給定key值定義的元素,有三種方法:      1)可以先使用c.count函式計算出元素個數,然後使用c.find(key)找到第一個元素,往後遞推c.count-1個元素即可遍歷全部所需元素。
     2)使用c.equal_range(key),該函式返回兩個迭代器,表示key值對應元素的起始迭代器(閉區間)和結束迭代器(開區間),然後從起始迭代器遍歷到結束迭代器即可。      3)使用c.upper_bound(key)和c.lower_bound(key),lower_bound求key值不小於給定值的第一個元素對應 的迭代器,upper_bound(key)求key值大於給定值的第一個元素對應的迭代器。然後通過這兩個函式返回的迭代器遍歷即可。 81,對於unordered容器,如果key值是自定義的,那麼需要在自定義型別實現hasher和eqOp兩個函式。
     比如:      size_t hasher(const ClassName &obj)
     {          return hash<string>() (obj.tostr());
     }
     bool eqOp(const ClassName &obj0, const ClassName &obj1)
     {          return obj0.tostr() == obj1.tostr();
     }
82, 在C++中,new分配變數空間時有點需要注意:T v = new T(),和T v = new T是不同的,前者是變數初始化,後者是預設初始化。所有型別都定義了變數初始化,自動將變數初始化為“空”,這個“空”對於不同型別對應不同的值,整數是 0,浮點是0.0,字串是“”。但是預設初始化就取決於型別是否有預設建構函式,如果有建構函式,那麼就會呼叫預設建構函式初始化v,否則v的值就是 undefined。build-in型別沒有預設建構函式,所以T v = new T,會使得v所指向的值為undefined。 83,delete只能對普通指標使用,不能對一般變數使用。 84, 智慧指標主要是在普通指標的基礎上封裝了一層,使得使用者對指標的使用更加方便和放心,在使用的過程中不用擔心指標因為釋放問題而導致的異常。在 C++11中,智慧指標主要有三種:shared_ptr<T> ptr, unique_ptr<T> ptr, weak_ptr<T> ptr;      shared_ptr<T> ptr的初始化可以通過以下幾種方式:      1)shared_ptr<T> ptr = make_shared<T>(args); //args的引數形式和T的建構函式一致。
     2)shared_ptr<T> ptr(q); //q可以是一個智慧指標或者普通指標(轉換成T*的也行,比如&v),還能是一塊新分配的記憶體。      3)shared_ptr<T> ptr = q; //q可以是智慧指標或者一般指標,但不能是一塊新分配的記憶體。      4)shared_ptr<T> ptr(u); //u是一個unique_ptr,此時u被被置位null,ptr指向u之前所指向的物件。      5)shared_ptr<T> ptr(q, d); //和方法2)類似,但是會用新的刪除函式d取代delete      但是智慧指標最好不要和不同指標混著用。      其它一些操作,如:     1)ptr = q;     //ptr指向q指向的物件,q必須是智慧指標。ptr和q的模版型別不一定要完全一樣,只要可以從q的模版型別轉向ptr的模版型別即可。此時ptr原來指向的物件引用計數減1,q指向的物件引用計數加1。     2)ptr.unique();//返回ptr所指向的物件引用數是否為1。     3)p.use_count(); //後者返回ptr所指向物件引用數。     與一般指標相比,shared_ptr主要可以用來防止記憶體洩漏和懸掛指標:     1)記憶體洩漏是指動態分配的記憶體被遺忘釋放了,導致這塊記憶體一直不能被回收,一般較難檢測出來,除非洩漏的記憶體非常多導致程式記憶體不夠用了。如果使用普通 指標,就可能導致這個問題,比如:T *p = new T(); T *p= new T(); 此時p第一次指向的記憶體塊就洩漏了。還有一種情況也可能導致記憶體洩漏:           T* f(T arg)            {               T *ptr = new T(arg);                ...;            }          //此時如果ptr不主動釋放他所指向的記憶體,等到出了函式f的範疇,ptr指向的空間就會出現記憶體洩漏。     而智慧指標本身也是一個物件,它有自己的解構函式,當智慧指標失效時,它會自動呼叫解構函式,刪除自身,並將所指向物件的引用值減1,引用值為0就會刪除所指向物件。     2)懸掛指標是指向一塊記憶體空間,這塊記憶體空間之前儲存一個物件,但是後來因為delete其他指標時被刪除了,此時該指標就是懸掛指標。而使用智慧指標,當刪除一個智慧指標時,首先將指向物件的引用值減1,如果此時引用值為0才會刪除該物件。     ptr.reset(); //是指ptr不再指向之前所指向的物件。     ptr.reset(q); //是指ptr不在指向之前所指向的物件,轉而指向q所對應或指向的空間。     ptr.reset(q, d); //同上,只是用d替換了delete函式,在必要時,會呼叫d刪除之前所指向的物件。     使用智慧指標需要注意以下幾點:     1)不要使用不同指標初始化或者reset多個智慧指標。     2)不要delete從get()返回的指標。     3)不要使用get()返回的指標去初始化其它智慧指標。     4)如果使用了get(),需要注意,智慧指標將所指空間內容刪除時,會使得get()返回的物件指標成為懸掛指標。     unique_ptr<T> ptr是一個可以保證只有一個unique_ptr指標指向一個物件,它的初始化方法只要有以下幾種:     1)unique_ptr<T> ptr(q); //q可以是一塊新分配的記憶體或者普通指標。     2)unique_ptr<T, D> ptr = ...; 和shared_ptr不同,unique_ptr需要指定自定義刪除函式型別的指標。     ptr.release(); //此時ptr不再指向之前所指向的物件,並返回之前所指向物件的指標,呼叫這個函式的時候,最好(務必)將函式的返回值存到某個指標,不然就記憶體洩漏了。     shared_ptr和unique_ptr幾個相同的函式:     1)p; //用p作為條件判斷指標p是否為空。     2)*p; //取p所指向的物件引用。     3) p->mem; //訪問p所指物件的成員變數。     4)p.get(); //返回所指物件的普通指標。     weak_ptr顧名思義,弱指標,將一個shared_ptr繫結到weak_ptr不會影響shared_ptr所指向物件的引用數。他主要有以下幾個方法:     1)weak_ptr<T> w = sp; 以及 w = sp; //將一個share_ptr繫結到weak_ptr。     2)weak_ptr<T> w(sp); //同上。     3)reset(); //釋放繫結的sp;     4)use_count(); //所指物件的引用數。     5)expired(); //判斷use_count()是否為0。0返回true,否則返回false。     6)lock(); //返回所繫結的一個shared_ptr,如果已經不存在,則返回一個空的shared_ptr。 85,在C++中,有些方法會被類預設定義,如果不想讓這些方法被定義,可以重新宣告這些函式,並在後面加 = delete;比如:ClassName(ClassName&) = delete; 這就意味著ClassName沒有拷貝建構函式,不能夠被拷貝,實現這一功能也可以將拷貝建構函式定義成private或者protected。 86,將拷貝建構函式定義成private的可以阻止該類物件的一切拷貝,如果試圖呼叫該類物件的拷貝建構函式,可能發生兩種錯誤:     1)編譯錯誤:當用戶程式碼試圖拷貝一個該類的物件,就會導致編譯錯誤。     2)連線錯誤:該類的成員函式或者友元函式(或者類)發生拷貝該類的拷貝時,就會導致連結錯誤。 87,運算子過載:C++中運算子過載時需要注意操作符的順序,有以下幾點需要注意:     1)賦值運算子(=),下標運算子([]),訪問運算子(()),以及成員訪問操作符(->)的運算子過載函式必須定義為成員函式。     2)複合賦值運算子(+=, -=)一般來說最好定義為成員函式,但不是必須的。     3)一些運算子可能改變物件的狀態,或者和給定型別相關的運算子(比如++, --, *)等,通常應該定義為成員函式。     4)對稱運算子(比如,算數運算子和關係運算符),通常需要定義成非成員函式。 88,輸入失敗通常有兩點原因:1)讀入錯誤型別的資料。2)遇到了EOF。 89,如果一個類需要重定義下標建構函式,那麼它需要實現兩個版本的下標建構函式:     1)返回普通的物件引用。     2)const型別的函式,並且返回const物件引用。     //用於const物件的呼叫。 90,過載前自增(自減)和後自增有以下區別:     1)宣告:前自增應該宣告為: ClassName& operator++(); 後自增應該宣告為:ClassName operator++(int);為了和前自增區別,後自增函式需要傳入一個整數,這個整數是什麼值都無所謂,但是必須傳入,一般來說最好不要使用這個整數,所以通常將該整數引數定義成匿名的。與前自增(返回物件引用)不同,後自增返回一個新的物件。     2)定義:          前自增:                  ClassName& operator++()                  {                         ++cur;                         return *this;                  }         後自增:                  ClassName operator++(int)                  {                         ClassName tmp = *this;                         ++(*this);                         return tmp;                  } 91,成員訪問操作符:有兩個:*和->,可以定義如下:     ClassName {          public:               string& operator*() const               {                    return (*p)[curr];                }               string& operator->() const               {                    return &(this->operator*();                }      } 92,函式訪問操作符,用法如下:     ClassName {         type operator()(args) const {              do something;          }      }     ClassName(args);即可訪問ClassName所過載的訪問操作符。     這種方法結合模版可以定義一系列的函式物件。比如plus<int> intAdd; int sum = intAdd(1, 2);     其它一些庫函式類(物件)有:less<Type>, greater<Type>等等,這兩個函式類可以用作比較器,比如:sort(num.begin(), num.end(), greater<int>()); //從大到小排序       sort(num.begin(), num.end(), less<int>()); //從小到大排序 93,lambda表示式是一個函式物件不是函式指標,普通定義的函式名可以當作函式指標使用,在使用中為了避免他們的差異,可以使用function物件,這個類是定義在function標頭檔案裡面,使用方法如下:     function<Type> f = func;     //Type是函式原型,可以自己指定,func是一個可訪問的物件(指標)。     f(args);     //args是func形參的例項化,此時呼叫f,就相當於呼叫func(args);     具體例子:     function<int(int, int)> f1 = add; // add是函式指標     function<int(int, int)> f2 = divide; // divide是一個函式物件類的一個物件     function<int(int, int)> f3 = []  (int i, int j) {return i*j;}      cout << f1(4, 2) << endl;     //輸出6     cout << f2(4, 2) << endl;     //輸出2     cout << f3(4, 2) << endl;     //輸出8 94,在C++中有的型別之間可以相互轉換,但是有的時候並不希望他們之間相互轉換,因此在C++11中,提供了一個新的關鍵字explicit,如果一個轉換操作符被宣告為explicit,那麼這個轉換操作只能顯示地轉換,不能隱式地轉換,除了一個例外,這個例外就是在條件語句中,即使操作已經被宣告為explicit,但還是可以隱式轉換。具體例子如下:     class SmallInt {           public:               explicit operator int() const {return val;}          }     SmallInt si = 3;     //這不是隱式型別轉換,是呼叫了建構函式,所以是對的。     si + 3;     //錯誤,原因需要隱式轉換成int才能進行運算。     static_cast<int> (si) + 3;     //可行,因為顯示轉換了。     一個常用的例子出現在輸入的時候:     while (cin >> value)      //這個例子中,條件語句會將cin隱式地轉換成bool型別,但是不能在其它情況下隱式地轉換。 95,靜態變數(函式)也可以繼承,並且可以通過類物件去訪問,但是無論怎麼整合,但是一個類(包括它的派生類)的一個靜態例項只存在一個,但是是共享的。 96,在C++11中,也加入了final關鍵字,如果一個類被宣告為final的,那麼他就不能再派生出子類。     比如: class Last final : Base {};     class Bad : Last {};     //就會導致編譯錯誤。 97,初始化時呼叫建構函式,賦值時呼叫賦值過載函式,可能同樣是等號,但是呼叫的函式不一樣。

相關推薦

**C++變數和基本內建型別**(C++ Primer讀書筆記

1.內建型別包括算數型別和空型別(void,適用於沒有任何返回值的函式或者其他的特殊場合)。算數型別包括整形(字元和布林型)和浮點型。 2.除了布林型和擴充套件的字元型之外。,其餘的整形都可以通過在前面新增unsigned來變成無符號型別(帶符號的型別可以表示負數,0,和整數。無符號型別

C++ primer 讀書筆記 9.2 容器庫概覽

模板類 list<int> deque<double> 也可以定義容器的容器, vector<string> vector<vector<string>> 迭代器 迭代器範圍 [begin, end)

C++ primer讀書筆記 7.4 類的作用域

一個類就是一個作用域,在類的外部,成員的名字被隱藏起來了 在類的外部定義成員函式時, 要加上類名和作用域運算子 而一旦遇到了類名,定義的剩餘部分在就在類的作用域之內了。包含引數列表和函式體 另一方面,函式的返回型別通常出現在函式名之前,因此如果返回類型別,需要明確指出哪個類定義了該

C++ primer 讀書筆記 第七章 02 訪問控制和封裝

在c++中,我們使用訪問說明符加強類的封裝性: 定義在public說明符之後的成員在整個程式內可被訪問,public成員是暴露在外的,決定了類的介面 定義在private說明符之後的成員可以被類的成員函式訪問,但不能被使用該類的程式碼訪問,private封裝了類的實現細節 一

C++ primer 讀書筆記 第七章 01 定義抽象資料型別

定義成員函式 成員函式的宣告必須在類的內部,它的定義既可以在類的內部也可以在類的外部。 struct Sales_data { std::string isbn() const { return bookNo; } double arv_price() co

C++ primer 讀書筆記 第七章 06 類的靜態成員

在成員的宣告之前加上static關鍵字即可宣告靜態成員 靜態成員的訪問 使用作用域運算子直接訪問 通過類的物件訪問 Account::rate(); Account ac1; Account *ac2 = &ac1; r = a

C++Primer讀書筆記十——泛型演算法.md

概述 在前一篇我們介紹了容器的基本概念以及使用其成員函式進行增刪改查,但有的時候我們還希望對容器進行更多的操作,比如:查詢特定元素,替換元素等。而標準庫並未給出此類成員函式, 此時需要引入algorithm標頭檔案,其中定義了一系列的操作演算法。 這些演算法不直

C++ primer讀書筆記 chapter3 標準庫型別

除第二章介紹的是C++的基本型別,本章將大致介紹一下C++定義的內容豐富的抽象資料庫型別標準庫。著重介紹一下sting、vector和bitset。 3.2標準庫string型別   1.string型別支援幾個建構函式。建構函式是一個特殊成員函式,定義如何初始化該型別的物件,以下是stri

C++Primer讀書筆記

動態型別:面向物件程式設計在執行時確定型別,泛型程式設計在編譯時獲知型別 模版:泛型程式設計基礎,一個建立類或函式的藍圖,適用於編譯時才確定類和函式型別的情況 模板定義:以template開始,後跟尖括號包圍的模板引數列表,內含一個或多個由逗號分隔的模板引數 例項化函式模板:呼叫模板時,隱式或顯示的指明模板實

C++ primer 讀書筆記

第六章 函式 傳入引用引數 吐槽:c++ primer 這本書感覺不行,關於很多概念講不清 同時代碼存在不具備連續以及調測性 #include "a.hpp" #i

c++ primer讀書筆記170119

重看了一遍第一章 問題1:c++中針對內建型別已經進行變數定義未初始化時的變數值處理。是否設定預設值? ①. 全域性變數編譯器會賦初值,區域性變數則需要自己初始化,否則編譯器報錯; ②. 全域性變數整型賦值系統賦初值為0,其他數值型別(float、lon

C++Primer讀書筆記

第1章 開始 1、C++程式 程式原始檔的名字,一般包括兩部分:檔名以及檔案字尾。檔案字尾一般用來標識檔案的內容 標頭檔案:一般以.h字尾結尾 程式文字檔案:C++一般以.cpp字尾結尾 C++標準庫中的名字都是在一個稱作std 的名字空間中宣告的,這些名字在我

C++ Primer 讀書筆記

1,命令編譯生成的預設輸出檔案(可執行檔案) 命名為:a.out(Unix), a.exe(Windows) 2,cout輸出首先會存到快取中,而printf之類的輸出會直接輸出到輸出流中。 3,可以從鍵盤上輸入End-Of-File:Ctrl+d(Unix), Ct

C++ Primer 讀書筆記2.1

1、C++中認為:void修飾返回值表示:不返回任何值;void修飾引數表列,表示:不接受任何引數、若引數表列裡什麼也不寫,表示:可接受任意型別的引數。 2、C++中規定short <= int <= long <= long long 3、注意隱式型別轉

C++ primer 讀書筆記

第三章字串 向量 陣列 string 初始化 初始化型別:拷貝初始化、直接初始化 使用=初始化即為,拷貝初始化。 char 陣列與string char 陣列如果沒有\0截止

C++Primer讀書筆記(二)

10.無符號數不會小於0   注意不能將帶符號型別和無符號型別混合使用。11.變數宣告與定義的關係   extern int i;//宣告i而非定義i(只宣告不定義用extern)   int j;//宣告並定義j   extern int i=1;//定義(賦值操作抵消了e

C++primer讀書筆記(一)

1.endl操縱符  效果:結束當前行,並將與裝置關聯的緩衝區(buffer)中的內容刷到裝置中。緩衝重新整理操作可以保證到目前為止程式所產生的所有輸出都真正寫入輸出流中,而不是僅停留在記憶體中等待寫入輸入流。//在除錯時新增列印語句,這類語句應該保證“一直”重新整理流。否則

[C/C++] C++ Primer學習筆記

轉義 寫到 十六進制 程序 結果 否則 筆記 end 情況 下面記錄我每天看書學到的以前不太清楚的概念和用法: Day 1 endl:具有輸出換行的效果,並刷新與設備相關聯的緩沖區。 註:在調試程序過程中插入的輸出語句都應刷新輸出流,否則可能會造成程序崩潰,將會導致程序出錯

C++ Primer 學習筆記_5_變量和基本類型(續2)

key 情況 boa 類和對象 類定義 優點 splay 查看 變量定義  變量和基本類型 七、枚舉 枚舉不但定義了整數常量集,並且還把它們聚集成組。 枚舉與簡單的const常量相比孰優孰劣, 通過以下一段代

《Programming in C讀書筆記

pro 標準 編譯 常量 第七章 自己的 編程錯誤 基礎 結構體使用   該書由美國Seephen G.Kochan著 賈洪峰譯,電子工業出版社,來源是九江學院圖書館采購,現在藏於九江學院圖書館逸夫樓。   本書的主要內容:   第一章、基礎知識   第二章、編譯和運行第一