C ++ primer
2 變量和基本類型
c++ 定義了一套包括算術(shù)類型和空類型(void)在內(nèi)的基本數(shù)據(jù)類型。
算術(shù)類型。
算術(shù)類型分為整型和浮點型。字符和布爾都算作整型。
C++規(guī)定了尺寸的最小值, char:1 short int:2 int:4 long int 4; float:4; double:8;long long int 8. bool類型最小尺寸未定義
帶符號和無符號類型。
字符類型被分為了三種,char、signed char、unsigned char。 char的類型和編譯器有關(guān),有時候是無符號,有時候是有符號。建議使用char的時候指定有符號,無符號。不要用char。
使用規(guī)則:
1. 明確知道結(jié)果不可能為負(fù)數(shù)時,使用無符號數(shù)。
2. 使用int進行整數(shù)計算。
3. 算術(shù)表達(dá)式中不要使用char或bool,存放時才使用。
4. 執(zhí)行浮點數(shù)運算選用double.float精度不夠而且與double計算代價相差無幾。
類型轉(zhuǎn)換
1. 非布爾—>布爾,0則為false,其他為true
2. 布爾–>非布爾,false 為0,true為1
3. 浮點數(shù)->整數(shù), 取小數(shù)點前面的那個
double a = 2177.20,a實際上可能為2177.1999,尤其是在qt里面使用double_Spinbox,需要進行精度補充。
5. 整數(shù)->浮點數(shù),小數(shù)部分設(shè)置為0
6. 給無符號數(shù)一個超出他表示范圍的值時,結(jié)果是對無符號類型表示的總數(shù)取mod。
浮點數(shù)比較:fabs(x)<1e-6
字面值常量:
形如42的值叫做字面值常量。每個字面值常量都對于一種數(shù)據(jù)類型。
整型:十進制:20 八進制:024 16進制:0X14
浮點型:3.14 3.14E0 0. 0e0 .001
字符:‘a(chǎn)’ ‘0’----48 ‘\0’ ---- 0 ‘a(chǎn)’----97 ‘A’-----65
字符串:“hello world” 末尾加一個空字符’\0’
轉(zhuǎn)義序列:不可打印(換行,回車等) 或特殊含義的字符
單引號('),雙引號("),反斜線(\),問號(?)
\123 八進制,超過3個其他不算 \x12 16進制
布爾字面值:true false
指針字面值:nullptr
c中是這樣定義NULL: #define NULL ((void *)0)
字符常量,用單引號括起來的單個普通字符或轉(zhuǎn)義字符
字節(jié)數(shù):4 int val = ‘p’
初始化和賦值:
列表初始化:
用{}來初始化變量。
默認(rèn)初始化:
如果定義時沒有初始化,則是默認(rèn)初始化,默認(rèn)值由變量類型以及定義變量的位置決定。 定義在函數(shù)體內(nèi)的內(nèi)置類型變量不被初始化,外的被初始化為0. 沒有被初始化,錯誤不可知。
變量聲明和定義。
程序分為多個文件,為了在文件間共享代碼,需要支持分離式編譯。聲明使得名字為程序所知,一個文件如果想要使用別處定義的名字則必須包含對那個名字的聲明。而定義負(fù)責(zé)創(chuàng)建與名字關(guān)聯(lián)的實體。
聲明:多個cpp文件,用同一個變量。
extern int i; 聲明 規(guī)定類型和名字,
extern int i = 0; 定義。 分配空間和初值。
類聲明:class 類名;
2.不完全類型只能在非常有限的情況下使用:可以定義指向這種類型的指針或引用,也可以作為一個已經(jīng)聲明(但沒有定義)的函數(shù)的參數(shù)或返回類型。
3.對于一個類來說,在創(chuàng)建它的對象前必須首先完成類的定義,而不能僅僅被聲明。否則編譯器就無法了解這樣的對象需要多少存儲空間。類似的,類也必須首先被定義,然后才能用引用或者指針訪問其成員。
4.對于類的靜態(tài)成員,直到類被定義之后數(shù)據(jù)成員才能被聲明成這種類類型。我們必須首先完成類的定義,然后編譯器才能知道存儲該數(shù)據(jù)成員需要多少空間。因為只有當(dāng)類全部完成后類才算被定義,所以一個類的成員類型不能是該類自己。然而,一旦一個類的名字出現(xiàn)后,它就被認(rèn)為是聲明過了(但尚未定義),因此類允許包含指向它自身類型的引用或指針。
函數(shù)聲明:
與定義的區(qū)別是沒有函數(shù)體,用分號替代。
可以不包含形參名字,但為了可讀性,最好還是寫上。
如果使用的僅僅是一個類的指針,沒有使用這個類的具體對象(非指針),也沒有訪問到類的具體成員,那么前置聲明就可以了。
復(fù)合類型:
引用
一般指左值引用,為對象起的別名。
右值引用:c++ 11新特性,用&&來獲得,只能綁定即將銷毀的對象。常數(shù)、表達(dá)式、函數(shù)返回值。主要是為了提高效率。相當(dāng)于延長了生命周期。
疑惑:return 做了什么? https://blog.csdn.net/jeff_/article/details/48915759
左值:變量等,等號左邊,長久保存。
右值:臨時值,等號右邊,短暫存在。
右值引用可以減少拷貝的次數(shù),提高效率。
std::move 將右值引用綁定到左值上。我們可以銷毀一個移后源對象,也可以賦予新值,但不能使用一個移后源對象的值。
對右值進行淺拷貝,右值對象中的指針直接賦值給新的對象,然后將右值對象的指針賦NULL.
指針:一個特殊的變量,存儲地址
遇到指針,都應(yīng)該問三個問題:指針的類型,指針?biāo)赶虻念愋?#xff0c;指針的值
nullptr指針作用:1、解決函數(shù)調(diào)用歧義 2、構(gòu)造函數(shù)重載
2個指針比較 == 指針的值比較。
復(fù)合類型的聲明:
從右往左讀,離變量名最近的符號對變量類型有最直接的影響。
指針數(shù)組 : *p[n]
數(shù)組指針 :(*p)[n] 可以指向一維數(shù)組,也可以指向二維數(shù)組,此時+1 表示到了下一行
二級指針:指向指針數(shù)組
sizeof 數(shù)組: 數(shù)組的大小 指針:4或8
const 限定符
const變量和普通的變量一樣,特殊的是初始化后值不能改變。
當(dāng)以編譯時初始化的方式定義一個const變量時,如const int bufSize = 512; 編譯的時候,會把所有用到該變量的地方替換對應(yīng)的值。默認(rèn)僅在當(dāng)前文件里有效,當(dāng)多個文件中出現(xiàn)了同名的const變量時,其實等同于在不同文件中分別定義了獨立的變量。
要多文件共享,需要定義和聲明的時候加extern。
const引用:通常情況下,引用只能綁定到對象上,而不能與字面值或某個表達(dá)式的結(jié)果綁定在一起。const 引用特殊,當(dāng)綁定到另一個類型時,會構(gòu)造一個臨時變量,而綁定到臨時變量上。
const 指針:int * const p; 從右往左讀,就近原則。
指向常量的指針: const int *p; 指針不能修改里面的值。
頂層const
頂層const表示指針本身是個常量,底層const表示指針?biāo)赶虻膶ο笫莻€常量。
頂層const可以表示任意對象是常量,底層const與指針和引用等有關(guān)。
拷貝操作時,底層const的限制不能忽視。拷入和拷出的對象必須有相同的底層const資格,或者2個數(shù)據(jù)類型必須能夠轉(zhuǎn)換。非常量可以轉(zhuǎn)換為常量,反過來不行。
頂層const不影響拷貝操作。
類型別名:
typedef;
定義類型別名。 與const結(jié)合會有意想不到的效果。 新的const 對象。
typedef char * pstring. const pstring cstr; cstr是一個指向char的常量指針。
##可以這樣來理解:typedef int integer;將typedef去掉,那就是個變量的定義,這兒即定義了一個int型的變量integer,考慮這個integer是什么類型的,那么這個typedef語句就是將integer定義為這個類型的。將typedef int (*func_pointer)(const&, int);中的typedef去掉,就成了一個函數(shù)指針定義,即func_pointer被定義為函數(shù)指針類型變量,那么原來的typedef即將func_pointer定義為函數(shù)指針類型.
auto:
讓編譯器通過初始值來推斷變量的類型(發(fā)生在編譯期)。 特殊:當(dāng)引用作為初始值時,真正參與的是引用對象的值。
1. 聲明為auto(不是auto&)的變量,忽視掉表達(dá)式頂層的const。即對有const的普通類型(int,double)忽視const,對常量指針(頂層const)變?yōu)槠胀ㄖ羔?#xff0c;對指向常量(底層const)的常量指針(頂層const)變?yōu)橹赶虺A康闹羔?#xff08;底層const)。
2. 聲明為auto&的變量,保持表達(dá)式的頂層const或volatile屬性
3. 若希望推導(dǎo)的是頂層const,加上const,即const auto。
容器:
特定類型對象的集合。
自定義數(shù)據(jù)結(jié)構(gòu):
數(shù)據(jù)結(jié)構(gòu)就是把一列相關(guān)的元素組合起來,然后使用它們的策略和方法。
定義類的時候,類體}后面要加上分號。定義一個類,也就是定義了一個類型。定義類型時一般不進行存儲分配。定義對象時將為其分配存儲空間。命名空間不需要加。
C++ 11新標(biāo)準(zhǔn),可以為類內(nèi)的數(shù)據(jù)成員提供一個類內(nèi)初始值。花括號或=,不可圓括號。
頭文件:預(yù)處理變量無視作用域。 ifndef,一般以類名大寫表示頭文件 _H.
#define 和 const區(qū)別:
1. define是在編譯的預(yù)處理階段起作用,而const是在 編譯
2. const有類型檢查,可以調(diào)試。
結(jié)構(gòu)體
1. 對齊字節(jié):
32位:4 64位:8 #progma pack(n) 指定對齊字節(jié)值
起始地址必須能整除成員變量的大小
結(jié)構(gòu)體的大小 必須是最大元素的整數(shù)倍
2. 共用體(聯(lián)合體) union 取最大值,所有成員相對于基址地址的偏移量為0。可以用來判斷大小端。大端:高字節(jié)放在低地址。小端,低字節(jié)放在低地址。
3. 枚舉 缺省值為0,1,2…;若賦值,自動加1
賦值時,需要賦枚舉里面的值,不能直接1,2
3 字符串、向量和數(shù)組
using聲明。
使用using聲明后,每次調(diào)用時就不需要再加前綴了。
using std::cin;
頭文件不應(yīng)該using聲明,可能會產(chǎn)生難以預(yù)料的名字沖突。
標(biāo)準(zhǔn)庫類型 string
string 表示可變長的字符序列。
1. 直接初始化和拷貝初始化。使用等號的是拷貝初始化,否則是直接初始化。
2. 讀寫string 對象。 cin >> str;
cin 時,string會自動忽略開頭的空白(空格,換行,制表符),遇到下一個空白停止。string對象返回運算符左側(cè)的運算對象作為結(jié)果,因此可以連續(xù)輸入。
cin >> str1 >> str2;
while ( cin >> str) 遇到文件結(jié)束符或非法輸入結(jié)束。
3. getline,遇到換行符結(jié)束,換行符會從緩沖區(qū)刷掉,不會寫入string里。
4. string::size_type, size的返回值,無符號類型。 不要和有符號比較。
5. string對象比較。 相同,短的< 長的。
6. string對象相加,與字面值混用時,+ 兩側(cè)必須有一個string。 字符串字面值和string是不同的類型。
7. 使用C++版本的C標(biāo)準(zhǔn)文件 #include cname 而不是name.h
8. 下標(biāo)訪問,[]返回的是該位置上字符的引用
9. size, length返回string真實長度,即便里面有\(zhòng)0,使用strlen可以截斷,去掉\0
標(biāo)準(zhǔn)庫類型vector
vector存放的是某種給定類型對象的可變長序列。
1. vector是類模板,需要傳入額外信息。 vector
2. 列表初始化 {} 如果不能列表初始化,則是值初始化。
vector v(10, “hi”);
3. 值初始化 () vector vec(10, -1); 生成10個-1的元素,如果沒寫-1,則為0. string則默認(rèn)初始化。
二維數(shù)組:vector<vector> vec(m, vector(n,0)); m*n的二維數(shù)組,所有元素都為0
4. []只能訪問,不能去添加元素。
5. 插入時寫emplace_back,而不是push_back;
push_back()右值時就會調(diào)用構(gòu)造函數(shù)和轉(zhuǎn)移構(gòu)造函數(shù)。需要先構(gòu)造臨時對象。
emplace_back在插入的時候直接構(gòu)造,就只需要構(gòu)造一次即可。不需要構(gòu)造臨時對象,右值引用參數(shù)。
若push_back/emplace_back傳遞的參數(shù)為已被構(gòu)造的對象,則沒有差別。
6.vec.size() 當(dāng)前容器所存儲的元素個數(shù)
7.vec.capacity() 容器在分配新的存儲空間之前能存儲的元素總數(shù)
6. 內(nèi)存擴充策略:滿了的時候,成倍擴充,然后拷貝原有數(shù)據(jù)到新內(nèi)存,釋放原內(nèi)存。
7. 內(nèi)存泄漏:clear()和erase(),resize()只改變size,不改變capacity。防止:ivec.swap(vector(ivec)); 定義一個臨時變量,交換內(nèi)容
8. vec.insert(vec.begin()+i,a);在第i個元素后面插入a;
9. 當(dāng)n大于當(dāng)前元素個數(shù),resize和reserve都會capacity。根據(jù)分配策略,可能會有更大的一塊。resize未指定參數(shù),按類型默認(rèn)初始化,添加元素。而reserve不會添加元素。
n < 當(dāng)前元素個數(shù),resize刪除多余的,capacity不改變。而reserve什么也不做。
10. find(vec.begin(), vec.end(), i) != vec.end();
11. vec.erease(vec.begin(), vec.begin()+1); 刪除第一個元素 左閉右開
12. pop_back() 刪除最后一個元素,盡量不要從中間刪除
測試網(wǎng)站:https://cpp.sh/
迭代器
1. 所有標(biāo)準(zhǔn)庫容器都可以使用迭代器,只有少數(shù)幾種支持下標(biāo)運算符。
2. v.end();表示尾元素的下一位置,當(dāng)容器為空時,begin==end
3. *iterator 返回所指元素的引用
4. iterator->mem 等價于 (*iterator).mem
5. 迭代器類型 iterator(讀寫) const_iterator(只讀) cbegin返回const_iterator.
6. erase刪除容器后,返回下一個迭代器。
數(shù)組
1. 定義的時候,數(shù)組維度必須可知。為常量表達(dá)式。
2. 數(shù)組未初始化的時候,為默認(rèn)初始化
3. 部分初始化的時候,類似vector值初始化,其他值采用默認(rèn)值
4. 數(shù)組不允許拷貝和賦值
5. 復(fù)雜的數(shù)組聲明,從數(shù)組的名字按照從內(nèi)向外的順序讀,先右后左。
6. c風(fēng)格字符串。strlen返回p的長度,空字符不計算在內(nèi),必須有空字符,不然會有錯誤。
使用string比c風(fēng)格更安全和高效。
7. 使用c_str(),返回指向一個空字符結(jié)尾的字符數(shù)組的常量指針。string內(nèi)部的,以size計算。盡量使用標(biāo)準(zhǔn)庫類型而非數(shù)組。
8. 多維數(shù)組,指的是數(shù)組的數(shù)組,按照名字從內(nèi)到外的順序閱讀。
4. 表達(dá)式
表達(dá)式由一個或多個運算對象組成,對表達(dá)式求值將得到一個結(jié)果。字面值和變量是最簡單的表達(dá)式,結(jié)果是他們的值。
左值和右值
右值的時候,用的是對象的內(nèi)容;左值的時候,用的是對象的身份。
算術(shù)運算符滿足左結(jié)合律,如果優(yōu)先級相同,按照從左往右的順序。
括號無視優(yōu)先級與結(jié)合律。
運算對象的求值順序和優(yōu)先級和結(jié)合律無關(guān)。優(yōu)先級只是規(guī)定了運算對象的組合方式。
int i = f1() * f2(); 未說明f1還是f2先計算
cout << i << ++ i << endl; 未定義
算術(shù)運算符—取模:
早期允許m%n的符號匹配n的符號,并且商向負(fù)無窮一側(cè)取整。C++新標(biāo)準(zhǔn)已經(jīng)禁用,除了-m導(dǎo)致溢出的特殊情況,其他時候:(-m)/n ,m/(-n) == -(m/n); m%(-n)=m%n;(-m)%n=-(m%n). 商一律向0取整。
邏輯和關(guān)系運算符
與和或都是先算左邊再算右邊,如果左邊可以確定整個的值,則不會計算右邊的(短路求值)。
關(guān)系運算符的求值結(jié)果是布爾值,if (i < j < k)
賦值運算符
賦值運算滿足右結(jié)合律,返回的是左側(cè)運算對象。
ival = ijal = 0; 都為0
優(yōu)先級低于關(guān)系運算符,if里面需要加括號
遞增和遞減運算符
前置版本,返回改變后的對象。
后置版本,返回改變前的對象。 (會生成一個副本,減低效率 j++,一般用前置版本)
使用*p++; 簡潔,遞增高于解引用。
*beg = toupper(*beg++); 未定義錯誤,不知道先算左邊的還是右邊的。
成員訪問運算符
p->mem 等價于(*p).mem 加括號是因為解引用優(yōu)先級低于.
條件運算符
cond ? exp1 : exp2
首先求cond的值,如果為真對exp1求值并返回其值,否則對exp2求值并返回其值。
在輸出表達(dá)式中運用條件運算符時,需要加括號,優(yōu)先級較低。
逗號運算符
常用于for循環(huán)中。
強制類型轉(zhuǎn)換
static_cast:把 expression 轉(zhuǎn)換為 type-id 類型,但沒有運行時類型檢查來保證轉(zhuǎn)換的安全性。
??主要用法如下:
????(1)用于類層次結(jié)構(gòu)中基類(父類)和派生類(子類)之間指針或引用的轉(zhuǎn)換。
????????進行上行轉(zhuǎn)換(把派生類的指針或引用轉(zhuǎn)換成基類表示)是安全的;
????????進行下行轉(zhuǎn)換(把基類指針或引用轉(zhuǎn)換成派生類表示)時,由于沒有動態(tài)類型檢查,所以是不安全的。
????(2)用于基本數(shù)據(jù)類型之間的轉(zhuǎn)換,如把int轉(zhuǎn)換成char,把int轉(zhuǎn)換成enum。這種轉(zhuǎn)換的安全性也要開發(fā)人員來保證。
????(3)把空指針轉(zhuǎn)換成目標(biāo)類型的空指針。
????(4)把任何類型的表達(dá)式轉(zhuǎn)換成void類型。
const_cast: static_cast 不能將 const int* 轉(zhuǎn)成 int*,const_cast 就可以. static_cast不支持底層const。
C語言的強制類型轉(zhuǎn)換有時候會有問題,推薦使用C++類型的。
5.語句
空語句
;加了空語句需要加注釋
條件語句
多層嵌套,可以提邏輯,減少嵌套層數(shù)
else與離他最近的未匹配的if匹配
switch語句
1. switch對表達(dá)式求值,然后值轉(zhuǎn)變?yōu)檎?br /> 2. case標(biāo)簽必須是整型常量表達(dá)式
3. 如果表達(dá)式和某個case匹配,直到switch結(jié)尾或遇到break結(jié)束。接著執(zhí)行switch之后的語句。
4. 一般加default,表示我們考慮到了這個情況。
5. switch內(nèi)部定義的變量,如果沒有初始化,其他分支可以用。初始化了其他分支不可以用。
迭代語句
1. while語句,定義在條件部分和循環(huán)體內(nèi)的變量,每次迭代都經(jīng)歷創(chuàng)建和銷毀的過程。
2. 傳統(tǒng)for語句。 for (init;condition;expression)
3. 范圍for語句。
for(declaration: expression).
expression必須是一個序列。
declaration定義一個變量,每次迭代都重新定義變量,并將其初始化序列中的下一個值。
對范圍for語句,不能增加vector對象的元素。因為for(auto r : v) 等價于 for(auto beg = v.begin(), end = v.end(); beg != end; ++beg)。
跳轉(zhuǎn)語句:
1. break語句,終止離最近的while for do while switch語句,并執(zhí)行其之后的第一條語句。
2. continue,終止里最的的循環(huán)中的當(dāng)前迭代并立即開始下一次迭代。對于傳統(tǒng)for,繼續(xù)執(zhí)行for語句頭的expression;對于范圍for,用序列中的下一個元素初始化循環(huán)控制變量。
只有switch嵌套在迭代語句里,才能用continue。
6.函數(shù)
函數(shù)是一個命名了的代碼塊,通過調(diào)用函數(shù)執(zhí)行對應(yīng)的代碼。函數(shù)可以重載,同一個名字可以對應(yīng)幾個不同的函數(shù)。
參數(shù)傳遞:
每次調(diào)用函數(shù)時會創(chuàng)建它的形參,并用傳入的實參對形參進行初始化,形參的初始化和變量的初始化一樣。
使用引用可以避免拷貝,提高效率,最好用常量引用。
盡量使用常量引用:1. 不使用常量引用會誤導(dǎo)讀者:函數(shù)可以改變引用的值 2. 非常量引用會縮小范圍,引發(fā)錯誤 3. 函數(shù)內(nèi)調(diào)用另一個函數(shù),無法直接使用。
數(shù)組形參:
int * int[] int[10] 都等價的,10只是表示期望,實際不影響
二維: int(*p)[10]; int p[][10] 第二個不可省略,一定要相等。
main命令行參數(shù):
prog -d -o -ofile daa0 argc = 5, argv[0]表示程序的名字,argv[1]表示實參。
返回值
void函數(shù),使用return; 末尾會自動執(zhí)行return;
有返回值的函數(shù),要寫return 1;不然會有未定義的錯誤.主函數(shù)main例外,如果結(jié)束的時候沒有,編譯器會隱式的插入一條返回0的return語句。
返回一個值的方式和初始化一個變量或形參的方式完全一樣,返回的值用于初始化調(diào)用點的一個臨時變量。
#### 不要返回局部對象的引用或指針,存儲空間被釋放,指向為無效的內(nèi)存區(qū)域。
調(diào)用運算符的優(yōu)先級和點和箭頭的相同,符合左結(jié)合律。
auto sz = shortString(s1, s2).size();
數(shù)組和函數(shù)無法拷貝。
聲明一個返回數(shù)組指針的函數(shù):Type (*fun(para))[length].
使用decltype: int odd[] = {1,2,1,3};
decltype(odd) *func(int i);
函數(shù)重載:
函數(shù)名相同,形參列表不同。返回值不同不構(gòu)成重載。
編譯器優(yōu)化,傳遞一個非常量對象的指針時,編譯器優(yōu)先選用非常量函數(shù)。。
如果在內(nèi)層作用域聲明名字,將隱藏外層作用域中聲明的同名實體,在不同作用域內(nèi)無法重載函數(shù)名。變量名也可隱藏函數(shù)名。
默認(rèn)實參:合理設(shè)計順序,默認(rèn)值的形參出現(xiàn)在后面。一般出現(xiàn)在聲明中,可以添加默認(rèn)實參(需要保證該右側(cè)的形參都有默認(rèn)值),但不能修改一個已經(jīng)存在的值。
string func(int, int, char = ‘c’);
string func(int, int, char = ‘a(chǎn)’); //錯誤,重復(fù)聲明
string func(int = 80, int = 80, char = ‘a(chǎn)’); // 正確
內(nèi)聯(lián)函數(shù):可避免函數(shù)調(diào)用的開銷。
是對編譯器發(fā)出的一個請求,編譯器可以忽略請求。
適用于規(guī)模較小,流程直接,頻繁調(diào)用的函數(shù)。一個75行的不太可能內(nèi)聯(lián)展開。
constexpr函數(shù):
在編譯的時候就能得到其返回值的函數(shù)。編譯器constexpr函數(shù)直接轉(zhuǎn)換成其返回值,因此,constexpr函數(shù)都是被隱式地定義為內(nèi)聯(lián)函數(shù)。使用constexpr關(guān)鍵字來修飾constexpr函數(shù)。
如constexpr int func(int i);
函數(shù)的返回類型和形參的類型必須是字面型類型,函數(shù)體內(nèi)有且只有一條return語句。
函數(shù)體內(nèi)可以包含其他語句,但需要不執(zhí)行任何操作,如空語句,類型別名等。
允許函數(shù)的返回值并非一個常量,當(dāng)實參非常量時。
調(diào)試幫助:
assert預(yù)處理宏,當(dāng)表達(dá)式結(jié)果為假時,輸出信息并終止程序的執(zhí)行。為真時,什么也不做。
NDEBUG預(yù)處理變量,assert行為依賴了這個變量,如果定義了這個,則assert什么也不做。可以使用這個變量編寫自己的條件調(diào)試代碼。
C++編譯器定義:
_ _ func _ 當(dāng)前函數(shù)的名字,const char 數(shù)組
C++預(yù)處理器定義:
_ _ FILE _ 存放文件名
_ _ LINE_ _ 存放當(dāng)前行號
_ _ TIME_ _ 存放文件編譯時間
_ _ DATE_ _ 存放文件編譯日期
函數(shù)匹配:
1. 選定本次調(diào)用的重載函數(shù)集,集中的函數(shù)稱為候選函數(shù)。需要與調(diào)用函數(shù)同名,聲明在調(diào)用點可見。
2. 根據(jù)實參,選出可行函數(shù)。需要形參數(shù)量與實參數(shù)量相同,實參與形參類型相同,或者能轉(zhuǎn)換為形參類型。
3. 從可行函數(shù)中尋找最佳匹配。
基本思想:實參類型與形參類型越接近,匹配越好。
含有多個形參的函數(shù)匹配。
1. 該函數(shù)每個實參的匹配性都不劣于其他可行函數(shù)需要的匹配
2. 至少有一個實參的匹配優(yōu)于其他可行函數(shù)的匹配。
如果檢查了所有實參之后沒有一個函數(shù)可以脫穎而出,則調(diào)用錯誤,編譯器會報二義性錯誤。
實參類型轉(zhuǎn)換等級
精確匹配:1)實參和形參類型相同。2)實參從數(shù)組類型或函數(shù)類型轉(zhuǎn)換為對于的指針類型。3)向?qū)崊⑻砑?刪除頂層const.
通過const轉(zhuǎn)換實現(xiàn)的匹配 將指向非常量類型的指針或引用轉(zhuǎn)換成對應(yīng)的常量類型的指針或引用。
非常量對象實參優(yōu)先使用非常量形參函數(shù)。
通過類型提升實現(xiàn)的匹配。 整型提升
通過算術(shù)類型轉(zhuǎn)換或指針轉(zhuǎn)換實現(xiàn)的匹配。
算術(shù)轉(zhuǎn)換是把一種算術(shù)類型轉(zhuǎn)換成另一種算術(shù)類型。
指針轉(zhuǎn)換:0或nullptr能轉(zhuǎn)換成任意指針類型。指向任意非常量的指針能轉(zhuǎn)換成void*.指向任意對象的指針能轉(zhuǎn)換成const void *;繼承關(guān)系間指針的轉(zhuǎn)換。
通過類類型轉(zhuǎn)換實現(xiàn)的匹配。
類型提升
ff(int); ff(short);
ff(‘c’) 直接調(diào)用ff(int);
算術(shù)類型:所有算術(shù)類型轉(zhuǎn)換的級別都一樣。
ff(long); ff(float);
ff(3.14); 錯誤,二義性調(diào)用。
函數(shù)指針
指向函數(shù),函數(shù)的類型與返回類型和形參類型共同決定,與函數(shù)名無關(guān)。
返回指向函數(shù)的指針
using f = int(int*,int); f是函數(shù)類型,不是指針
using pf = int (*p)(int *, int) pf是指針類型。
返回類型不會自動地轉(zhuǎn)換為指針,需要我們顯式的轉(zhuǎn)換。
pf f1(int);正確
f f1(int); 錯誤,f是函數(shù)類型
f * f1(int); 正確,函數(shù)指針。
7.類
類的基本思想是數(shù)據(jù)抽象和封裝。
1)數(shù)據(jù)抽象是一種依賴于接口和實現(xiàn)分離的編程技術(shù)。類的接口包括用戶所能執(zhí)行的操作;類的實現(xiàn)包括類的數(shù)據(jù)成員、負(fù)責(zé)接口實現(xiàn)的函數(shù)體以及各種私有函數(shù)。
2)封裝實現(xiàn)了類的接口和實現(xiàn)的分離。封裝后的類隱藏了它的實現(xiàn)細(xì)節(jié),即用戶只能使用接口而無法訪問實現(xiàn)部分。
類想要實現(xiàn)數(shù)據(jù)抽象和封裝,首先需要定義一個抽象數(shù)據(jù)類型。在抽象數(shù)據(jù)類型中,由類的設(shè)計者負(fù)責(zé)類的實現(xiàn)過程;使用該類的程序員則只需要抽象地思考類型做了什么,而無需了解類型的工作細(xì)節(jié)。-----數(shù)據(jù)抽象。
定義在類內(nèi)部的函數(shù)是隱式的內(nèi)聯(lián)函數(shù)。
成員函數(shù)含有this指針,this是一個常量指針,指向這個對象,無法修改。頂層const ,const靠近指針。
1)const成員函數(shù),const關(guān)鍵字隱式修改this指針的類型。
由data * const p 變?yōu)?const data * const p;
2)const成員函數(shù)無法改變對象的內(nèi)容
3)常量對象,以及常量對象的引用和指針都只能調(diào)用常量成員函數(shù)。
4)const成員函數(shù)的定義也要在后面加const
構(gòu)造函數(shù):
1)構(gòu)造函數(shù)負(fù)責(zé)類的初始化
2)構(gòu)造函數(shù)沒有返回值
3)構(gòu)造函數(shù)不能聲明為const。 當(dāng)創(chuàng)建一個const對象時,直到構(gòu)造函數(shù)完成初始化后,對象才獲得const屬性。
默認(rèn)構(gòu)造函數(shù)
1)默認(rèn)構(gòu)造函數(shù)就是在調(diào)用時不需要顯示地傳入實參的構(gòu)造函數(shù)。無參數(shù)的”和“帶缺省參數(shù)的都是,不要同時出現(xiàn)。一般情況下,默認(rèn)構(gòu)造函數(shù)是一個空的函數(shù)體。
2)如果類沒有顯示的定義構(gòu)造函數(shù),則編譯器會隱式的定義一個默認(rèn)構(gòu)造函數(shù),稱為“合成的默認(rèn)構(gòu)造函數(shù)”。
a.如果存在類內(nèi)的初始值,則用它來初始化
b.否則,默認(rèn)初始化該成員。內(nèi)置類型在函數(shù)體內(nèi)未知值。如果類的對象沒有顯式地初始化,則其值由類確定。
不能依賴于合成的默認(rèn)構(gòu)造函數(shù)
想要合成的默認(rèn)構(gòu)造函數(shù)。
sales_data() = defaultl;
如果沒有在構(gòu)造函數(shù)的初始化列表中顯示地初始化成員,則該成員將在構(gòu)造函數(shù)體之前執(zhí)行默認(rèn)初始化。
如果成員是const、引用或者屬于某種未提供默認(rèn)構(gòu)造函數(shù)的類類型,則必須通過構(gòu)造函數(shù)初始化列表為這些成員提供初值。
建議使用構(gòu)造函數(shù)初始值:1.底層效率問題 2.一些數(shù)據(jù)成員必須初始化,可以避免某些意想不到的編譯錯誤。
成員初始化順序與在類內(nèi)定義的出現(xiàn)順序一致,與初始化列表的順序無關(guān)。
sales_data obj(); //聲明了一個函數(shù)而非對象
只接受一個實參的隱式轉(zhuǎn)換機制:
class默認(rèn)訪問權(quán)限是私有,struct默認(rèn)訪問權(quán)限是公有。
友元:
類可以允許其他類或者函數(shù)訪問它的非公有成員。友元,增加一條以friend開頭的函數(shù)聲明。
一般最好在類定義開始結(jié)束前的位置集中聲明友元。
友元的聲明僅僅指定了訪問權(quán)限,而非一個通常意義上的函數(shù)聲明。如果希望類能夠調(diào)用函數(shù),則要再加個函數(shù)聲明。
友元類:友元類的成員函數(shù)可以訪問包括非公有成員在內(nèi)的所有成員。
友元關(guān)系不具備傳遞性。
每個類負(fù)責(zé)控制自己的友元類或友元函數(shù)。
類的其他特性:
可變數(shù)據(jù)成員:變量聲明成mutable,任何時刻都可以更改它,即便是在const成員函數(shù)里。
類的聲明:
可以只聲明類而暫時不定義它,稱為前向聲明。稱為不完全類型,可以用于:定義指向這種類型的指針或引用,也可以聲明(不可以定義)以不完全類型作為參數(shù)或者返回類型的函數(shù)。
類的作用域:
一個類就是一個作用域,在類外定義成員函數(shù)時必須同時提供類名和函數(shù)名。在類的外部,成員的名字被隱藏起來了。
一旦遇到類名,定義的剩余部分就在類的作用域之內(nèi)的。這里指參數(shù)列表和函數(shù)體。
函數(shù)的返回類型在函數(shù)名之前,所以要加作用域。
類的靜態(tài)成員:
8. IO庫
9. 順序容器
順序容器:
通常,使用vector是最好的選擇,除非有更好的理由選擇其他容器。
迭代器:左閉右開區(qū)間。
類型別名:使得使用和機器無關(guān)
size操作返回是string::size_type類型的值。
difference_type使用來表示兩個迭代器之間的距離的。
對于+1,-1問題,如果2邊是閉區(qū)間,則是right-left+1;如果有一端是開的,則right-left。
value_type:元素類型。
每個STL容器類,都有一句相同的代碼:typedef T value_type;
typedef關(guān)鍵字就是給這些數(shù)據(jù)類型起一個別的名字,然后用這個自己起的名字來定義變量。
用處:1. 應(yīng)該使用指示了大小和符號typedef來代替基本類型,見名知意。
reference:元素左值引用。
begin和end
begin返回第一個元素的迭代器,end返回尾元素之后位置的迭代器。
r返回反向迭代器,c返回const迭代器。
當(dāng)不需要寫訪問時,應(yīng)使用cbegin和cend.
vector a (10); 10個元素值為0
array
array<int, 10>,使用時必須指定元素類型和大小。
內(nèi)置數(shù)組無法進行拷貝和對象賦值操作,但是array可以。需要保證元素類型和大小一致。
賦值和swap:
assign僅適用于順序容器,會替換原來的值,不能傳遞給調(diào)用assign容器的迭代器。
swap交換2個相同類型容器的內(nèi)容。不會進行元素拷貝,除array外,不會導(dǎo)致指向容器的迭代器、引用和指針失效。
容器元素時拷貝,當(dāng)我們用一個對象初始化容器時,或講一個對象插入到容器時,我們實際上放入的是對象值的拷貝,而不是對象。
insert將元素插入到所指定的位置之前。
同一次插入多個值時,不會改變順序。
iter = lst.insert(iter, word); 多次push_front;
返回的是新插入元素的迭代器
front,back,下標(biāo)和at返回的是引用。
c.front() = 42;
auto &v = c.back(); 可以改變
auto v2 = c.back(); 無法改變
at可以對越界進行檢查,越界會拋出out_of_range異常。下標(biāo)訪問不會。
c.erease(p);刪除p所指定的元素,返回一個指向被刪除元素之后元素的迭代器。
如果迭代器失效,為安全考慮,不要再使用它。
管理容量的成員函數(shù):
c.shrink_to_fit(); 只適用于vector。string和deque。將capacity減少為size大小。只是一個請求,標(biāo)準(zhǔn)庫并不保存歸還內(nèi)存。
capacity和reserve只適用于vector和string。
c.capacity();不重新分配內(nèi)存空間,c可以保存元素個數(shù)。
c.reserve(n);分配至少能容納n個元素的內(nèi)存空間。
resize只改變size,不改變capacity。
額外的string操作:
substr, 返回一個string,是原始string的一部分或全部的拷貝。
傳遞一個開始位置和計數(shù)值。
string s1 = s.substr(0, 5);如果開始位置大于s大小,會拋出out_of_range異常。如果開始位置+計數(shù)值大于s大小,則只會拷貝到結(jié)尾。
find返回一個npos,定義為-1;
auto f = s.find(“name”);
數(shù)值轉(zhuǎn)換:
如果string不能轉(zhuǎn)換為一個數(shù)值,會拋出invalid_argument異常。如果轉(zhuǎn)換得到的數(shù)值無法用任何類型來表示,會拋出out_of_range異常。
11.關(guān)聯(lián)容器
關(guān)聯(lián)容器的元素是按照關(guān)鍵字來保存和訪問的。
2個主要的關(guān)聯(lián)容器:map和set.
map中的元素是一些關(guān)鍵字-值(key-value)對,關(guān)鍵字起到索引的作用,值則表示與索引相關(guān)聯(lián)的數(shù)據(jù)。
set中每個元素只包含一個關(guān)鍵字,支持高效的關(guān)鍵字查詢操作:檢查一個給定關(guān)鍵字是否在set中。
標(biāo)準(zhǔn)庫提供了8個關(guān)聯(lián)容器,1. 關(guān)鍵字是否可以重復(fù) 2. 是否有序。
multimap表示可以重復(fù),unordered_map表示無序。
map是關(guān)鍵字-值對的集合,set是關(guān)鍵字的集合。
pair類型:
一個pair保存2個數(shù)據(jù)成員。
pair<T1, T2> p;進行值初始化
pair<T1, T2> p(v1, v2); first是v1, second是v2
make_pair(v1, v2); 返回一個用v1和v2初始化的pair,類型通過v1和v2的類型推斷得到。
p1 relop p2; relop表示關(guān)系運算符,只有first和second同時滿足時,才滿足。
關(guān)聯(lián)容器操作:
key_type: 關(guān)鍵字類型
mapped_type:關(guān)鍵字關(guān)聯(lián)的類型,只適用于map
value_type: set是key_value;
map是pair<const key_value, mapped_type>因為關(guān)鍵字不能改,所以是const.
當(dāng)解引用一個迭代器時,返回的是value_type類型的引用。
map的first不可更改。
set的迭代器都是const,不可更改值。
通常不對關(guān)聯(lián)容器使用泛型算法。
添加元素:
刪除元素:
1 . erase函數(shù),返回刪除元素的個數(shù)。
map下標(biāo)操作:
map和unordered_map容器提供了下標(biāo)運算符和一個對應(yīng)的at函數(shù)。set不支持下標(biāo),因為沒有與關(guān)鍵字對應(yīng)的值。multimap也不支持小標(biāo)操作,因為有多個值與關(guān)鍵字對應(yīng)。
map進行下標(biāo)操作時,如果關(guān)鍵字不再map里,則會添加到map中,并進行值初始化。所以只能對非const的map使用下標(biāo)操作。
c[k]; 返回關(guān)鍵字為k的元素;如果k不在c中,添加一個關(guān)鍵字為k的元素,并進行值初始化。
c.at(k);訪問關(guān)鍵字為k的元素,帶參數(shù)檢查;若k不在c中,拋出一個out_of_range異常。
訪問元素:
find,count,count會返回次數(shù),如果不需要計數(shù),最好用find.
對map使用find代替下標(biāo)操作。
無序容器:
不是使用比較運算符來組織元素,而是使用一個哈希函數(shù)和關(guān)鍵字類型的==運算符。
無序容器在存儲上組織為一組桶,每個桶保存零個或多個元素。無序容器使用一個哈希函數(shù)將元素映射到桶。無序函數(shù)的性能依賴于哈希函數(shù)的質(zhì)量和桶的數(shù)量和大小。
12. 動態(tài)內(nèi)存
在C++中,動態(tài)內(nèi)存管理是通過一對運算符完成的。new,在動態(tài)內(nèi)存中為對象分配一個空間并返回一個指向該對象的指針。delete,接受一個動態(tài)對象的指針,銷毀該對象,并且釋放與之相關(guān)的內(nèi)存。
shared_ptr允許多個指針指向同一個對象。unique_ptr則獨占所指向的對象。weak_ptr是伴隨類,是一種弱引用,指向shared_ptr所管理的對象。
shared_ptr類
與vector一樣,使用的時候需要傳入指針可以指向的類型。
每個shared_ptr都有一個引用計數(shù),當(dāng)我們用一個shared_ptr初始化另一個shared_ptr時,或?qū)⑺鳛橐粋€參數(shù)傳遞給一個函數(shù),以及作為函數(shù)的返回值時,它所關(guān)聯(lián)的引用計數(shù)就會遞增。當(dāng)我們給一個shared_ptr賦予新值或者shared_ptr被銷毀時,引用計數(shù)會遞減。
每次shared_ptr銷毀時,會調(diào)用shared_ptr的析構(gòu)函數(shù),將引用計數(shù)-1,如果此時變?yōu)?,則會銷毀對象并釋放所占用的內(nèi)存。
使用動態(tài)內(nèi)存的一個常用原因是允許多個對象共享相同的狀態(tài)。
直接管理內(nèi)存
使用new動態(tài)分配內(nèi)存和初始化對象。
int *p1 = new int; 默認(rèn)初始化,值未定義
int *p2 = new int(); 值初始化為0;
int *p3 = new (nothrow) int; 如果分配失敗,會返回一個空指針,不會拋出bad_alloc異常。
釋放動態(tài)內(nèi)存
delete會銷毀給定指針指向的對象;釋放對應(yīng)的內(nèi)存。
傳遞的指針必須指向動態(tài)分配的內(nèi)存,或者是一個空指針。釋放一塊并非new分配的內(nèi)存或者將相同的指針值釋放多次,行為是未定義的。
delete之后,指針變?yōu)閼铱罩羔?#xff0c;指向一塊曾經(jīng)保存數(shù)據(jù)對象但現(xiàn)在已經(jīng)變得無效的內(nèi)存的指針,應(yīng)該delete后將其賦nullptr。
接收指針參數(shù)的智能指針構(gòu)造函數(shù)是explicit的,必須使用直接初始化的方式。
shared_ptr p1= new int(1024); 錯誤;
shared_ptr<int p2(new int (1024)); 正確。
不要混合使用普通指針和智能指針
如果混合使用的話,智能指針自動釋放之后,普通指針有時就會變成懸空指針,當(dāng)將一個shared_ptr綁定到一個普通指針時,我們就將內(nèi)存的管理責(zé)任交給了這個shared_ptr。一旦這樣做了,我們就不應(yīng)該再使用內(nèi)置指針來訪問shared_ptr所指向的內(nèi)存了。
也不要使用get初始化另一個智能指針或為智能指針賦值
unique_ptr
某個時刻只能有一個unique_ptr指向一個給定對象,由于一個unique_ptr擁有它指向的對象,因此unique_ptr不支持普通的拷貝或賦值操作。
不能拷貝unique_ptr有一個例外:我們可以拷貝或賦值一個將要被銷毀的unique_ptr.最常見的例子是從函數(shù)返回一個unique_ptr.
weak_ptr
weak_ptr是一種不控制所指向?qū)ο笊嫫诘闹悄苤羔?#xff0c;它指向一個由shared_ptr管理的對象,將一個weak_ptr綁定到一個shared_ptr不會改變shared_ptr的引用計數(shù)。一旦最后一個指向?qū)ο蟮膕hared_ptr被銷毀,對象就會被釋放,即使有weak_ptr指向?qū)ο?#xff0c;對象還是會被釋放。
weak_ptr的操作
由于對象可能不存在,我們不能使用weak_ptr直接訪問對象,而必須調(diào)用lock,此函數(shù)檢查weak_ptr指向的對象是否存在。如果存在,lock返回一個指向共享對象的shared_ptr,如果不存在,lock將返回一個空指針。
動態(tài)數(shù)組:
進度:看到423頁。
需要融合:
4. 在可以使用const的時候,都要添加const
5. const函數(shù)定義的時候,也要加const
總結(jié)
以上是生活随笔為你收集整理的C ++ primer的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 1叉叉加速器分析_加速分析
- 下一篇: 与你分享五句话(转贴)