C++笔记汇总
C++基礎(chǔ)
本文是在學(xué)習(xí)B站黑馬C++課程時(shí)記錄。另有部分知識(shí)點(diǎn)為海賊班胡船長(zhǎng)所教授。在此表達(dá)對(duì)他們的誠(chéng)摯的感謝。
1、C++的左值右值:
首先了解引用的概念:引用是一個(gè)變量,它引用其他變量的 內(nèi)存位置。
int x = 34 ; int &IRef = x;在代碼中,IRef 就是一個(gè)引用。在聲明中,引用是通 & 符號(hào)來修飾的。它出現(xiàn)在類型與變量的標(biāo)識(shí)符之間,這種類型的引用稱為左值引用。
左值:可以看作是一個(gè)關(guān)聯(lián)了名稱的內(nèi)存位置,允許程序的其他部分來訪問它。名稱:任何可用于訪問內(nèi)存位置的表達(dá)式。“指向指定的內(nèi)存,以變量形式存在” “相當(dāng)于容器”
右值:右值是一個(gè)臨時(shí)值,它不能被程序的其他部分訪問。“不指向任何地方” “相當(dāng)于容器內(nèi)的事物”
int x = 0; x = 5; x = sqrt(5);x是一個(gè)左值,這是因?yàn)閤代表一個(gè)內(nèi)存位置,它可以被程序的其他部分訪問。
表達(dá)式sqrt(5)時(shí)一個(gè)右值,因?yàn)樗砹艘粋€(gè)由編譯器創(chuàng)建的臨時(shí)內(nèi)存位置,以保存由函數(shù)返回的值。該內(nèi)存位置僅被訪問一次。在此之后它就會(huì) 立即被刪除,再也不能被訪問。
C++11右值引用的概念:表示一個(gè)本應(yīng)該沒有名稱的臨時(shí)對(duì)象。右值引用的聲明與左值引用類似,但是它使用的是2個(gè)&符號(hào)&&。臨時(shí)對(duì)象最多可以有一個(gè)左值引用指向它。
int && rRef = sqrt(5); cout << rRedf2、C++中常量的定義:
兩種形式:
使用宏定義
#define Day 7 // 注意不要加分號(hào)使用const修飾符
const int Day = 7; // 注意要加分號(hào)3、實(shí)數(shù)
小數(shù)默認(rèn)是 雙精度double類型,因此在設(shè)置float類型時(shí)候,常在數(shù)字末尾添加f
float x = 3.14f;精度
| 字節(jié) | 4 | 7 |
| 有效位數(shù) | 8 | 15~16 |
科學(xué)計(jì)數(shù)法
float f = 3e34、sizeof()
可以查看占用內(nèi)存空間
5、字符
字符要用單引號(hào)。字符占用1個(gè)字節(jié)
轉(zhuǎn)意字符: \a 警報(bào) \t \n ? \"
6、字符串
C語(yǔ)言風(fēng)格:注意變量名后面加中括號(hào)[],要用雙引號(hào)。
char 變量名[] = "...hellO"C++風(fēng)格string:
#include <string> String 變量名 = "hello world"7、布爾類型
bool flag = true; //注意全小寫,與python不同 bool key = false;8、邏輯運(yùn)算符
運(yùn)算符:
非 ! , 與 && , 或 ||
9、 程序流程結(jié)構(gòu)
順序結(jié)構(gòu)、選擇結(jié)構(gòu)、循環(huán)結(jié)構(gòu)
if語(yǔ)句:注意與python的不同,條件表達(dá)式在括號(hào)內(nèi),且不加:。
三目運(yùn)算符: 返回值的可以是變量,即左值
//條件表達(dá)式1?表達(dá)式2:表達(dá)式3 int a = 0; int b = 20; (a < b ? a: b) = 100; cout << a << b << endl;switch語(yǔ)句:注意在case后面使用break
switch(表達(dá)式) {case 結(jié)果1: // 注意類型,如果“結(jié)果1”是整型,傳入字符型的會(huì)導(dǎo)致出錯(cuò)。cout<< "best" << endl; // 注意不加中括號(hào)break;case 結(jié)果2:cout<< "good" << endl;break;default:cout<< "bad" << endl;break; }優(yōu)點(diǎn):結(jié)構(gòu)清晰,執(zhí)行效率高
缺點(diǎn):判斷時(shí)只能時(shí)整型或者字符型,不能判斷一個(gè)區(qū)間。
for語(yǔ)句:循環(huán)。注意C++中基于范圍的for循環(huán)如何使用。
while語(yǔ)句:循環(huán)
do while語(yǔ)句:先執(zhí)行一次循環(huán)語(yǔ)句,然后再判斷循環(huán)條件
10、關(guān)于數(shù)字的個(gè)位數(shù)、十位數(shù)計(jì)算
案例描述:計(jì)算三位數(shù)字的個(gè)位、十位、百位數(shù)計(jì)算:
int x = rand()%1000 + 1 int a, b, c; a = x % 10; b = x /10 % 10; //先出發(fā) 后取模 c = x /100;11、跳轉(zhuǎn)語(yǔ)句
break, continue:跳出當(dāng)前循環(huán)
goto語(yǔ)句:
FLAG: //標(biāo)記定義 cout << "jump to here"<< endl; goto FLAG; //跳轉(zhuǎn)到FLAG位置12、數(shù)組
數(shù)組定義的三種形式
數(shù)據(jù)類型 數(shù)組名 [數(shù)組長(zhǎng)度];
數(shù)據(jù)類型 數(shù)組名 [數(shù)組長(zhǎng)度] = {值1, 值2 ...};
數(shù)據(jù)類型 數(shù)組名 [ ] = {值1, 值2 ...}
數(shù)組的輸出:使用for循環(huán)進(jìn)行輸出。
sizeof(arr):可以統(tǒng)計(jì)整個(gè)數(shù)組在內(nèi)存中的長(zhǎng)度
數(shù)組名是一個(gè)常量不能進(jìn)行復(fù)制操作。
13、 隨機(jī)數(shù)種子
srand(1); int x = rand() % 100 + 1;C++ 函數(shù)
1、函數(shù)的定義
函數(shù)返回值 函數(shù)名 (參數(shù)列表){//函數(shù)體語(yǔ)句return 表達(dá)式; }值傳遞: 就是函數(shù)調(diào)用時(shí)實(shí)參將 數(shù)值 傳入給形參。如果形參發(fā)生改變,并不回影響實(shí)參。
函數(shù)聲明:聲明可以有多次,定義只能有一次。
2、函數(shù)的分文件編寫
作用:讓代碼更清晰
頭文件 .h 中寫 函數(shù) 的聲明
頭文件中不能寫函數(shù)的定義,以防止重定義。
但是頭文件中可以寫 類 的定義。
在源文件 .cpp 或 .cc 中寫 函數(shù) 的定義
正確理解頭文件:#include "***.h"頭文件不進(jìn)行編譯,而是在預(yù)編譯階段,將***.h的 內(nèi)容 拷貝到引用位置,然后進(jìn)行源文件的編譯。
3、內(nèi)聯(lián)函數(shù) inline
內(nèi)聯(lián)函數(shù)在聲明時(shí),使用關(guān)鍵字inline
內(nèi)聯(lián)的意義:在編譯時(shí),讓編譯器用該部分語(yǔ)句去替換函數(shù)表達(dá)式,節(jié)省了形參傳遞。
注意事項(xiàng):
C++ 指針
1、定義和使用指針
// 數(shù)據(jù)類型 *指針變量名; int a = 10; int *p = &a; // 將a的地址復(fù)制給指針p cout <<"a的地址是:"<< &a << endl; cout <<"指針p是:" << p << endl; // 指針前加*, 代表解引用。 cout << "*p的值是:" << *p <<endl; *p = 1000; cout << "*p的值是:" << *p <<endl;2、指針的大小
32位系統(tǒng):指針占4個(gè)字節(jié)空間大小。0x0000
64位系統(tǒng):占8個(gè)字節(jié)空間大小
3、空指針和野指針
空指針nullptr:用來給指針變量進(jìn)行初始化。并且空指針是不可以訪問的。
// 空指針的幾種形式 int *p = nullptr; int *p = 0; inr *p = NULL;野指針:指向沒有訪問權(quán)限的位置的指針。
int *p = (int*)0x1100;4、const修飾指針
const修飾指針的三種情況:
常量指針 — const int *指針名 又名 指向常量的指針
特點(diǎn):指針的指向可以修改,但是指針指向的地址的值不可以修改。
// 常量指針 int a = 10, b = 20; const int *p = &a; // 常量指針的定義 *p = &b; // 正確,指針的指向可以修改 *p = 100; // 錯(cuò)誤,指針指向的值不可以被修改指針常量 — int * const p
特點(diǎn):指針的指向不可以修改,但是指針指向的值可以修改
// 指針常量 int * const p = &a;const即修飾指針,又修飾常量
const int* const p
特點(diǎn):指針的指向和指針指向的值都不可以被修改
5、指針和函數(shù)
地址傳遞,可以修改實(shí)參。
C++結(jié)構(gòu)體
結(jié)構(gòu)體屬于用戶自定義的數(shù)據(jù)類型,允許用戶存儲(chǔ)不同的基礎(chǔ)數(shù)據(jù)類型。
建議:將結(jié)構(gòu)體的定義放在頭文件中
1、結(jié)構(gòu)體的定義和使用
語(yǔ)法: struct 結(jié)構(gòu)體名 {結(jié)構(gòu)體成員列表};
通過結(jié)構(gòu)體創(chuàng)建變量的方式有三種:
struct 結(jié)構(gòu)體名 變量名
struct 結(jié)構(gòu)體名 變量名 = {成員1值, 成員2值}
定義結(jié)構(gòu)體時(shí)順便創(chuàng)建變量
struct關(guān)鍵字在創(chuàng)建變量時(shí)可以省略。
#include<string> // 結(jié)構(gòu)體定義 // 創(chuàng)建學(xué)生數(shù)據(jù)類型:學(xué)生(姓名, 年齡,分?jǐn)?shù)) struct Student{// 成員列表string name;int age;int score; }s3; // 3 定義結(jié)構(gòu)體時(shí)順便創(chuàng)建變量 //另外注意結(jié)構(gòu)體后面要使用分號(hào)// 創(chuàng)建結(jié)構(gòu)體類型變量 int main(){// 1 struct 結(jié)構(gòu)體名 變量名struct Student s1; // struct關(guān)鍵字在創(chuàng)建變量時(shí)可以省略// 給s1屬性復(fù)制。通過點(diǎn)“.”操作符訪問結(jié)構(gòu)體變量中的屬性s1.name = "東七七";s1.age = 21;s2.score = 77;// 2 struct 結(jié)構(gòu)體名 變量名 = {成員1值, 成員2值}struct Student s2 = {"James", 34, 91} }2、結(jié)構(gòu)體數(shù)組
作用: 將自定義的結(jié)構(gòu)體放入到數(shù)組中方便維護(hù)
語(yǔ)法: struct 結(jié)構(gòu)體名 數(shù)組名[元素大小] = { {},...,{} };
struct Student stuArray[10]={{"James", 34, 91},...}3、結(jié)構(gòu)體指針
作用:通過指針訪問結(jié)構(gòu)體中的成員
利用操作符 -> 可以通過結(jié)構(gòu)體指針訪問結(jié)構(gòu)體屬性
struct Student *p = &s1; p->name = "Alike"; p->age = 42;4、 嵌套結(jié)構(gòu)體
作用: 結(jié)構(gòu)體中的成員可以是另一個(gè)結(jié)構(gòu)體
例如:每一個(gè)老師輔導(dǎo)一個(gè)學(xué)員, 一個(gè)老師的結(jié)構(gòu)體中,記錄一個(gè)學(xué)生的結(jié)構(gòu)體
// 學(xué)生結(jié)構(gòu)體定義 struct Student{string name;int age;int score; } // 教師結(jié)構(gòu)體定義 struct Teacher{int id;string name;int age;struct Student stu; }5、結(jié)構(gòu)體做函數(shù)參數(shù)
作用:將結(jié)構(gòu)體作為參數(shù)傳入
傳遞方式: 值傳遞,地址傳遞
// 值傳遞 void printStuInfo(Student stu){// 函數(shù)體 } // 地址傳遞 void printStuInfo_ptr(const Student *ptr){ //定義常量指針,使得ptr指向的值不可以被修改//函數(shù)體ptr->name; // 注意要用->訪問結(jié)構(gòu)體成員 }6、結(jié)構(gòu)體中const的使用
作用:用const修飾防止被錯(cuò)誤修改
void printStuInfo_ptr(const Student *ptr){//函數(shù)體 ptr->name; // 注意要用->訪問結(jié)構(gòu)體成員 }C++核心編程
1、內(nèi)存分區(qū)模型
C++程序在執(zhí)行時(shí), 將內(nèi)存大方向劃分為4個(gè)區(qū)域
代碼區(qū):存放函數(shù)體的二進(jìn)制代碼,由操作系統(tǒng)進(jìn)行管理
全局區(qū): 存放全局變量和靜態(tài)變量以及常量
棧區(qū): 由編譯器自動(dòng)分配釋放,存放函數(shù)的參數(shù)值,局部變量等
堆區(qū):由程序員分配和釋放,若程序員不釋放,程序結(jié)束時(shí)由操作系統(tǒng)回收
內(nèi)存四區(qū)的意義:
不同區(qū)域存放的數(shù)據(jù),賦予不同的生命周期,給我們更大的靈活編程。
程序運(yùn)行前:
在程序編譯后,生成了exe可執(zhí)行程序,未執(zhí)行該程序前分為兩個(gè)分區(qū)
代碼區(qū):
? a. 存放CPU執(zhí)行的機(jī)器指令
? b. 代碼區(qū)是 共享 的,目的是對(duì)于頻繁被執(zhí)行的程序,只需要在內(nèi)存中有一份代碼即可
? c. 代碼區(qū)是 只讀 的,目的是防止程序意外地修改了它的指令
全局區(qū):
? a. 存放全局變量(注意是在main函數(shù)之外定義的)和靜態(tài)變量
? b. 全局區(qū)還包含了常量區(qū),字符串常量和其他常量也存放在此
? c. 該區(qū)域的數(shù)據(jù)在程序結(jié)束后由操作系統(tǒng)釋放
? d. 局部變量、const修飾的局部變量(局部常量) 不在全局區(qū),而在棧區(qū)
// 查看局部變量和全局變量的地址 // 全局變量 int g_a = 10; int g_b = 10;int main(){int a = 10;int b = 10:// 打印地址會(huì)發(fā)現(xiàn), a、b的地址在一個(gè)區(qū)間內(nèi) g_a、g_b的地址在另一個(gè)區(qū)間內(nèi) }程序運(yùn)行后:
棧區(qū):
由編譯器自動(dòng)分配釋放,存放函數(shù)的參數(shù)值、局部變量等。
注意事項(xiàng):不要返回局部變量的地址,棧區(qū)開辟的數(shù)據(jù)由編譯器自動(dòng)釋放。
int * func() //形參數(shù)據(jù)也會(huì)放到棧區(qū) {int a = 10; // 棧區(qū)的數(shù)據(jù)在函數(shù)執(zhí)行完自動(dòng)釋放。return &a; // 有錯(cuò)誤,但能通過編譯。返回了局部變量地址。 } int main(){int *p = func(); //接收func()的地址cout << *p << endl; // 第一次沒問題,因?yàn)榫幾g器做了保留cout << *p << endl; // 第二次出現(xiàn)亂碼 }堆區(qū):
由程序員分配釋放,若程序員不釋放,程序結(jié)束時(shí)由操作系統(tǒng)回收。
在C++中主要利用new來開辟堆區(qū)內(nèi)存,利用delete釋放
int *func(){// 利用new關(guān)鍵字 可以將數(shù)據(jù)開辟到堆區(qū)// 指針 本質(zhì)也是局部變量, 放在棧上,指針保存的數(shù)據(jù)時(shí)放在堆區(qū)int *p = new int(10);return p; // 返回10在堆區(qū)地址, 且該地址由程序員使用delete才能釋放或者程序結(jié)束釋放 } int main(){int *p = func();cout << *p << endl; }語(yǔ)法: new 數(shù)據(jù)類型
返回值: new 返回時(shí)該數(shù)據(jù)類型的指針
new(10): 創(chuàng)建int類型的元素, 10表示值, 釋放用delete
new[10]: 在堆區(qū)開辟數(shù)組,10表示10個(gè)元素, 釋放用delete[]
// 1. new 的基本語(yǔ)法 int * func(){int *p = new int(10); // new返回的是int的指針,指向新分配元素的位置,位置的值為10;return p; } delete p; // 2. 在堆區(qū)利用new開辟數(shù)組 int * func2(){int* arr =new int[10]; // 10代表數(shù)組有10個(gè)元素return arr } delete[] arr; // 釋放數(shù)組,需要加[]2、引用
作用: 給變量起別名,注意數(shù)據(jù)要相同, 因?yàn)椴粫?huì)發(fā)生數(shù)據(jù)類型隱式轉(zhuǎn)換
語(yǔ)法: 數(shù)據(jù)類型 &別名 = 原名;
注意事項(xiàng):
引用定義時(shí)必須進(jìn)行初始化
引用一旦初始化綁定后,就不能改變綁定對(duì)象了。
引用做函數(shù)參數(shù) :
作用: 函數(shù)傳參時(shí),可以利用引用的技術(shù)讓形參修飾實(shí)參
優(yōu)點(diǎn): 可以簡(jiǎn)化指針修改實(shí)參
// 交換函數(shù) // 1、值傳遞 void swap(int a, int b) { int temp = a; a = b; b = temp; } // 2、地址傳遞 void swap(int *a, int *b) { int temp = *a; *a = *b; *b = temp; } // 3、引用傳遞 void swap(int &a, int &b) { int temp = a; a = b; b = temp; }引用做函數(shù)返回值 :
作用: 引用是可以作為函數(shù)的返回值存在
注意:不要返回局部變量的引用
用法:函數(shù)的調(diào)用可以作為左值
int& func(){static int a = 10; // 靜態(tài)變量,存放在全局區(qū),全局區(qū)上的數(shù)據(jù)在程序結(jié)束后系統(tǒng)釋放return a; } int p = func(); // 整個(gè)是復(fù)制操作, p的值與a相同,但是地址不同 int &r = func(); // r與a的值和地址都相同 func() = 1000; // 函數(shù)的調(diào)用作為左值。 過程: 返回靜態(tài)常量a的引用, 然后通過該引用修改a的值為1000。引用的本質(zhì) : 是一個(gè)指針常量。指針操作都由編譯器操作了。
// 引用的本質(zhì) int &r = a; // 實(shí)際被自動(dòng)轉(zhuǎn)換為 int* const ref = &a; 指針常量是不可更改的,這也說明了為什么引用不可更改 r = 30; // 內(nèi)部發(fā)現(xiàn)r是引用,自動(dòng)轉(zhuǎn)換為 *ref = 20; 引用的值不是常量,所以可以更愛常量引用 :
作用: 常量引用主要用來修飾形參,防止誤操作
在函數(shù)形參列表中,可以加 const 修飾形參, 防止形參改變實(shí)參
// 常量引用 int & ref = 10; // 錯(cuò)誤, 引用必須引用一塊合法的內(nèi)存空間 const int & ref = 10; // 正確 // 加入const之后,編譯器將代碼修改為 int temp = 10; const int & ref = temp; ref = 20; //錯(cuò)誤, 加入const之后變?yōu)橹蛔x,不可以修改// 打印數(shù)據(jù)的函數(shù) void showValue(const int & value){cout << value << endl; }3、函數(shù)提高(默認(rèn)、占位、重載)
函數(shù)的默認(rèn)參數(shù):
函數(shù)的形參列表中的形參是可以有默認(rèn)值的。
語(yǔ)法: 返回值類型 函數(shù)名 (參數(shù)= 默認(rèn)值) { 函數(shù)體 }
// 如果某個(gè)位置已經(jīng)有了默認(rèn)參數(shù),那么從這個(gè)位置往后,從左到右都必須有默認(rèn)值 int func(int a; int b = 1; int c =2){return 0; }注意:如果 函數(shù)聲明 有默認(rèn)參數(shù),函數(shù)定義 就 不能 有默認(rèn)參數(shù)。因?yàn)闀?huì)出現(xiàn)重定義。
函數(shù)的占位參數(shù):
函數(shù)的形參列表里可以有占位參數(shù),用來做占位,調(diào)用函數(shù)時(shí)必須補(bǔ)充該位置。
語(yǔ)法: 返回值類型 函數(shù)名 (數(shù)據(jù)類型) {函數(shù)體};
// 占位參數(shù) void func(int a, int){ // 第二個(gè)參數(shù) 是占位參數(shù) return ; } // 調(diào)用 func(1,2); // 調(diào)用函數(shù)時(shí)必須補(bǔ)充該位置 // 占位參數(shù)可以有默認(rèn)參數(shù) void func2(int a, int = 10){ return ;}函數(shù)重載:
作用: 函數(shù)名可以相同,提高復(fù)用性
函數(shù)重載滿足條件:
同一個(gè)作用域下
函數(shù)名稱相同
函數(shù)的 參數(shù)類型不同 或者 個(gè)數(shù)不同 或者 順序不同(需要是不同類型)。
注意:函數(shù)的返回值 不可以作為函數(shù)重載的條件
// 函數(shù)重載 void func(){// 函數(shù)體 } void func(int a){ // 函數(shù)體 } void func(int a, string b){// 函數(shù)體 } void func(string b, int a){// 函數(shù)體 }函數(shù)重載的注意事項(xiàng):
C++ 類和對(duì)象
C++面向?qū)ο蟮奶匦?#xff1a;抽象、封裝、繼承、多態(tài)
1、封裝
作用:
語(yǔ)法:class 類名{ 訪問權(quán)限: 屬性 / 行為 };注意最后的分號(hào),默認(rèn)訪問權(quán)限是private。
術(shù)語(yǔ):
三種訪問權(quán)限:
| public | 公共權(quán)限 | 😀 | 😀 | |
| protected | 保護(hù)權(quán)限 | 😀 | 😈 | 繼承時(shí),兒子可以訪問父親中的保護(hù)內(nèi)容 |
| private | 私有權(quán)限 | 😀 | 😈 | 繼承時(shí),兒子不可以訪問父親中的私有內(nèi)容 |
Struct 和 Class 的區(qū)別:
唯一區(qū)別:默認(rèn)訪問權(quán)限不同
成員屬性設(shè)置為私有:
案例1:
// 設(shè)計(jì)立方體類 // 屬性:長(zhǎng) 寬 高 // 行為:獲取立方體的面積 獲取立方體的體積 // 分別利用全局函數(shù)和成員函數(shù)判斷兩個(gè)立方體是否相等 #include<iostream> #include<string> using namespace std;class Cube { public:// 設(shè)置 長(zhǎng)void setL(int l) { m_L = l; }// 獲取 長(zhǎng)int getL() { return m_L; }// 設(shè)置 寬void setW(int w) { m_W = w; }// 獲取 寬int getW() { return m_W; }// 設(shè)置 高void setH(int h) { m_H = h; }// 獲取 高int getH() { return m_H; }// 獲取立方體的面積int getArea() {return 2 * (m_L * m_W + m_L * m_H + m_W * m_H);}// 獲取立方體的體積int getVolume() {return m_L * m_W * m_H;}// 判斷兩個(gè)立方體是否相等bool m_isSame(Cube& c2) {if (m_L == c2.getL() && m_W == c2.getW() && m_H == c2.getH())return true;return false;} private:int m_L; // 長(zhǎng)int m_W; // 寬int m_H; // 高 }; // 判斷兩個(gè)立方體是否相等 bool isSame(Cube& c1, Cube& c2) {if (c1.getL() == c2.getL() && c1.getW() == c2.getW() && c1.getH() == c2.getH())return true;return false; }int main(){Cube c1;c1.setL(10);c1.setW(10);c1.setH(10);cout << "c1的面積是:" << c1.getArea() << endl;cout << "c1的體積是:" << c1.getVolume() << endl;Cube c2;c2.setL(10);c2.setW(10);c2.setH(10);cout << "c1、c2是否相等:" << c1.m_isSame(c2) << endl;cout << "c1、c2是否相等:" << isSame(c1, c2) << endl;Cube c3;c3.setL(10);c3.setW(9);c3.setH(10);cout << "c1、c3是否相等:" << c1.m_isSame(c3) << endl;cout << "c1、c3是否相等:" << isSame(c1, c3) << endl;return 0; }案例2:
#include<iostream> #include<string> using namespace std;class Point { public:void setX(int x) { m_x = x; }void setY(int y) { m_y = y; }int getX() { return m_x; }int getY() { return m_y; } private:int m_x;int m_y; };class Circle { public:void setR(int r) { m_r = r; }int getR() { return m_r; }void setCenter(Point center_point) { center = center_point; }Point getCenter() { return center; } private:int m_r;Point center; // 在圓的對(duì)象內(nèi)部使用了點(diǎn)Point對(duì)象來構(gòu)建圓心 }; int main() {Circle c1;Point center; // 圓心點(diǎn)center.setX(3);center.setY(4);c1.setR(9);c1.setCenter(center); }類的多文件結(jié)構(gòu):
// 頭文件 Point.h #program once #include<iostram> using namespace std;// 僅使用類的聲明就可 class Point { public:void setX(int x) ;void setY(int y) ;int getX() ;int getY() ; private:int m_x;int m_y; }; // 源文件 Point.cpp 要使用 作用域限定符"::"來限定是Point類的成員函數(shù) #include "Point.h" void Point::setX(int x) { m_x = x; } void Point::setY(int y) { m_y = y; } int Point::getX() { return m_x; } int Point::getY() { return m_y; }2、對(duì)象的初始化和清理
作用:C++中采用構(gòu)造函數(shù)和析構(gòu)函數(shù)來初始化對(duì)象和銷毀對(duì)象。
2.1 構(gòu)造函數(shù)和析構(gòu)函數(shù)概念
這兩個(gè)函數(shù)會(huì)被編譯器 自動(dòng)調(diào)用,完成對(duì)象的初始化和清理工作。對(duì)象的初始化和清理是編譯器強(qiáng)制要求我們做的事情,因此如果我們不提供構(gòu)造和析構(gòu),編譯器會(huì)提供編譯器提供的構(gòu)造函數(shù)和析構(gòu)函數(shù)是空實(shí)現(xiàn)。
構(gòu)造函數(shù)的語(yǔ)法:類名(){}
析構(gòu)函數(shù)的語(yǔ)法:~類名(){}
2.2 構(gòu)造函數(shù)的分類及調(diào)用
兩種分類方式:
1. 按參數(shù)分為: 有參構(gòu)造和無參構(gòu)造(默認(rèn)構(gòu)造) 2. 按類型分類: 普通構(gòu)造和拷貝(復(fù)制)構(gòu)造(拷貝構(gòu)造也是一種構(gòu)造函數(shù),用戶沒有調(diào)用的話,編譯器會(huì)自動(dòng)聲成)三種調(diào)用方式:
注意事項(xiàng) :
2.3 拷貝構(gòu)造函數(shù)的調(diào)用時(shí)機(jī)
C++拷貝構(gòu)造函數(shù)調(diào)用實(shí)際通常有三種情況:
使用一個(gè)已經(jīng)創(chuàng)建完畢的對(duì)象來初始化一個(gè)新對(duì)象
Person p2(p1);值傳遞方式給函數(shù)參數(shù)傳值
void func(Person p){// 函數(shù)體 }以值方式返回局部對(duì)象
Person func(){Person p1;return p1; }? 拷貝構(gòu)造函數(shù)的參數(shù)為什么是常量引用? :
2.4 構(gòu)造函數(shù)調(diào)用規(guī)則
默認(rèn)情況下,C++編譯器至少給一個(gè)類添加3個(gè)函數(shù)
構(gòu)造函數(shù)調(diào)用規(guī)則如下:
2.5 深拷貝與淺拷貝(面試內(nèi)容)
淺拷貝:簡(jiǎn)單的賦值拷貝操作。增加了一個(gè)指針,指向原來已經(jīng)存在的內(nèi)存。
深拷貝:在堆區(qū)重新申請(qǐng)空間,進(jìn)行拷貝操作。增加了一個(gè)指針,并新開辟了一塊空間。
問題:淺拷貝帶來的問題是堆區(qū)的內(nèi)存重復(fù)釋放。
解決方法**:自己實(shí)現(xiàn)拷貝構(gòu)造函數(shù)**,新開辟堆區(qū)空間,來解決淺拷貝帶來的問題。 即深拷貝
如果屬性有在堆區(qū)開辟的,一定要自己提供拷貝構(gòu)造函數(shù),防止淺拷貝帶來的問題
(理解時(shí)可以寫程序逐語(yǔ)句F11調(diào)試一下)
深拷貝: 當(dāng)被復(fù)制的對(duì)象數(shù)據(jù)成員是指針類型時(shí),不是復(fù)制該指針成員本身,而是將指針?biāo)笇?duì)象進(jìn)行復(fù)制。
2.6 初始化列表
作用:C++提供了初始化列表語(yǔ)法,用來初始化屬性
語(yǔ)法:構(gòu)造函數(shù)(): 屬性1(值1), 屬性2(值2) ... {}
class Person{ public:// 傳統(tǒng)初始化操作Person(int a, int b, int c){m_A = a;m_B = b;m_C = c;}// 初始化列表初始化屬性 Person(int a, int b, int c):m_A(a),m_B(b),m_C(c){}// 注意冒號(hào)“:”的位置,在參數(shù)列表之后。int m_A;int m_B;int m_C; };2.7 類對(duì)象作為類成員
C++類中的成員可以是另一個(gè)類的對(duì)象,我們稱該成員為對(duì)象成員。
class A{} class B{A a; }B類中有對(duì)象A作為成員,A為對(duì)象成員
// 當(dāng)有其他類對(duì)象作為本類成員,構(gòu)造時(shí)先構(gòu)造類對(duì)象,再構(gòu)造自身; 析構(gòu)順序是先進(jìn)(自身)后出(其他類對(duì)象)2.8 靜態(tài)成員
靜態(tài)成員就是在成員變量和成員函數(shù)前加上關(guān)鍵字static,稱為靜態(tài)成員
靜態(tài)成員分為:
靜態(tài)成員變量
- 所有對(duì)象共享同一份數(shù)據(jù)
- 在編譯階段分配內(nèi)存(提前了)
- 類內(nèi)聲明,類外初始化。
- 可以通過類名直接訪問靜態(tài)成員變量
- 私有靜態(tài)成員不能被外部訪問
靜態(tài)成員函數(shù)
- 所有對(duì)象共享同一個(gè)函數(shù)
- 靜態(tài)成員函數(shù) 只能 訪問靜態(tài)成員變量
- 也是有訪問權(quán)限的
3、 C++ 對(duì)象模型和this指針
3.1 成員變量和成員函數(shù)分開存儲(chǔ)
在C++中,類內(nèi)的成員變量和成員函數(shù)分開存儲(chǔ)。
只有非靜態(tài)成員變量才存儲(chǔ)在類的對(duì)象上
// 空對(duì)象 class Person {}; Person p; // 占用內(nèi)存空間 sizeof(p) 為1 // C++ 編譯器會(huì)給每個(gè)空對(duì)象也分配一個(gè)字節(jié)空間,是為了區(qū)分空對(duì)象占內(nèi)存的位置 // 每個(gè)空對(duì)象也應(yīng)該有一個(gè)獨(dú)一無二的內(nèi)存地址 // 非靜態(tài)成員 class Person{ int a; }; Person p; // 占用內(nèi)存空間 sizeof(p) 為4 // 非靜態(tài)成員函數(shù) 存儲(chǔ)不在對(duì)象上 class Person{ void setA(int a) {};int a; }; Person p; // 占用內(nèi)存空間 sizeof(p) 為4 // 靜態(tài)成員 存儲(chǔ)不在對(duì)象上 class Person{ int a; static int b;}; Person p; // 占用內(nèi)存空間 sizeof(p) 為4,3.2 this指針
每一個(gè) 非靜態(tài)成員函數(shù) 只會(huì)誕生一份函數(shù)實(shí)例,也就是說多個(gè)類型的對(duì)象會(huì)共用一塊代碼
這一塊代碼是如何區(qū)分哪個(gè)對(duì)象調(diào)用自己?
C++ 提供 this 指針來區(qū)分,this指針指向被調(diào)用的成員函數(shù)所屬的對(duì)象。
this指針:
用途:
當(dāng)形參和成員變量同名時(shí),可以用this指針來區(qū)分
在類的非靜態(tài)成員函數(shù)中返回對(duì)象本身,可以用return *this。
3.3 空指針訪問成員函數(shù)
C++中空指針也是可以調(diào)用成員函數(shù)的,但是也要注意有沒有用到this指針
如果用到this指針,需要加以判斷保證代碼的健壯性
// 空指針調(diào)用成員函數(shù) class Person { public:void showClassName() {cout << "Class name is Person!" << endl;}void showAge() {if (this == nullptr) { return; }cout << "Age is : " << m_age << endl; // m_age實(shí)際上是 this->m_age。 因此如果對(duì)象是空指針就需要加以判斷}int m_age = 20; }; // 調(diào)用 int main() {Person *p = nullptr;p->showClassName();p->showAge();return 0; }3.4 const修飾成員函數(shù)
常函數(shù):
常對(duì)象:
4、 友元
在程序里,有些私有屬性也想讓類外特殊的一些函數(shù)或者類進(jìn)行訪問,就需要用到友元技術(shù)
目的:讓一個(gè)函數(shù)或者類,可以訪問另一個(gè)類中的私有成員
關(guān)鍵字:friend
友元的三種實(shí)現(xiàn):
- 全局函數(shù)做友元 , 可以將全局函數(shù)放在類內(nèi)實(shí)現(xiàn)
- 類做友元
- 成員函數(shù)做友元
類做友元:
語(yǔ)法: friend class 類名;
類做友元會(huì)用到 前向引用聲明:(多類相互引用的情況,類先聲明后使用)
注意事項(xiàng):
成員函數(shù)做友元
語(yǔ)法: friend 返回值類型 類名::函數(shù)名();
#include<iostream> #include<string> using namespace std;class Building; // 前向引用聲明。 class GoodGay { public:GoodGay();void visit(); // 訪問Building屬性中的私有成員void visit2(); // 不能訪問Building屬性中的私有成員Building* building; };class Building {friend void GoodGay::visit(); // 成員函數(shù)作為友元 public:Building();string m_SittingRoom;private:string m_BedRoom; };GoodGay::GoodGay() {building = new Building; }void GoodGay::visit() {cout << "GoodGay正在visit:" << building->m_SittingRoom << endl;cout << "GoodGay正在visit:" << building->m_BedRoom << endl; }void GoodGay::visit2() {cout << "GoodGay正在visit:" << building->m_SittingRoom << endl;// cout << "GoodGay正在visit:" << building->m_BedRoom << endl; 不能訪問 } Building::Building() { //構(gòu)造函數(shù)在類外實(shí)現(xiàn){m_SittingRoom = "客廳";m_BedRoom = "臥室";} }int main() {GoodGay g;g.visit();g.visit2();return 0; }5、 運(yùn)算符重載
概念: 對(duì)已有運(yùn)算符進(jìn)行重新定義,賦予其另一種功能,以適應(yīng) 不同的數(shù)據(jù)類型。
5.1 加號(hào)運(yùn)算符重載
operater
運(yùn)算符重載也可以發(fā)生函數(shù)重載
總結(jié):
對(duì)于內(nèi)置數(shù)據(jù)類型的運(yùn)算符是不能重載的
不要濫用運(yùn)算符重載
5.2 左移運(yùn)算符重載
作用:重載左移運(yùn)算符 配合友元 可以實(shí)現(xiàn)輸出自定義數(shù)據(jù)類型
// 左移運(yùn)算符重載 不能通過成員函數(shù)實(shí)現(xiàn),只能利用全局函數(shù)重載左移運(yùn)算符 // 訪問私有類時(shí),可以將全局函數(shù)聲明為友元函數(shù)。 class Person {friend ostream& operator<<(ostream& cout, Person& p); public:// 利用成員函數(shù)重載 左移運(yùn)算符// 不會(huì)利用成員函數(shù)重載左移<<運(yùn)算符,因?yàn)闊o法實(shí)現(xiàn)cout為左值Person(int a, int b) {this->m_A = a;this->m_B = b;} private:int m_A;int m_B; };// 只能利用全局函數(shù)重載左移運(yùn)算符 // 不能用void 用 ostream類 ostream & operator<<(ostream& cout, Person p) {cout << "m_A = " << p.m_A << " m_B = " << p.m_B;return cout; } int main() {Person p(10,10);return 0; }5.3 遞增運(yùn)算符重載
作用:通過重載遞增運(yùn)算符,來自己實(shí)現(xiàn)的整型數(shù)據(jù)類型
class MyInteger { public:MyInteger(){m_Num = 0;}// 重載前置運(yùn)算符遞增 返回引用是為了一直只對(duì)一個(gè)對(duì)象修改MyInteger& operator++(){m_Num++;return *this;}// 重載后置運(yùn)算符遞增// void operator++(int) int表示占位參數(shù),可以用于區(qū)分前置和后置遞增// 注意:這里只能用 intMyInteger operator++(int) { // 先記錄當(dāng)前結(jié)果MyInteger temp = *this; // 局部非靜態(tài)變量 因此返回值不是引用。// 后遞增m_Num++;// 最后將記錄結(jié)果返回return temp; // }int m_Num = 0; };// 為什么使用后置遞增運(yùn)算 用引用傳參&myint會(huì)報(bào)錯(cuò) ostream& operator<<(ostream& out, MyInteger myint) {out << myint.m_Num << endl;return out; }int main() {MyInteger myint;cout << ++myint << endl;cout << myint << endl;MyInteger myint2 = myint++;cout << myint2 << endl;cout << &(myint++) << endl;return 0; }為什么使用后置遞增運(yùn)算 用引用傳參 MyInteger &myint會(huì)報(bào)錯(cuò)?
因?yàn)楹笾眠f增返回臨時(shí)對(duì)象,不能綁定到非常量引用。所以那個(gè)形參必須寫成const引用。打印函數(shù)不會(huì)更改對(duì)象,所以最好要寫成const形式。通常來說const形參適用面廣一些(常量,非常量都能傳進(jìn)去)。第二,推薦ostream& operator<<形參別寫成cout,造成誤導(dǎo)。
加入const之后,編譯器將代碼修改為:
MyInteger temp = 后置遞增的值; const MyInteger & myint = temp;只有傳入的是臨時(shí)對(duì)象時(shí),才發(fā)生復(fù)制構(gòu)造。
5.4 賦值運(yùn)算符重載
C++ 編譯器至少給一個(gè)類添加4個(gè)函數(shù):
如果類中有屬性指向堆區(qū),做賦值操作時(shí)也會(huì)出現(xiàn)深淺拷貝問題。
// 賦值運(yùn)算符重載 #include<iostream> #include<string> using namespace std;class Person { public:Person(int age) {m_Age = new int(age);}~Person() {if (m_Age != nullptr) {delete m_Age;m_Age = nullptr;}}Person &operator=(Person& p) {// 編譯器是提供淺拷貝// m_Age = p.m_Age;// 應(yīng)該先判斷是否有屬性在堆區(qū),如果有先釋放干凈,然后再深拷貝if (m_Age != nullptr) {delete m_Age;m_Age = nullptr;}// 深拷貝m_Age = new int(*p.m_Age);//返回對(duì)象自身return *this;}int* m_Age; }; int main() {Person p1(18);cout << "p1的年齡是:" << *p1.m_Age << endl;Person p2(20);cout << "p2的年齡是:" << *p2.m_Age << endl;p2 = p1;cout << "p2的年齡是:" << *p2.m_Age << endl;Person p3(60);p3 = p2 = p1;cout << "p3的年齡是:" << *p3.m_Age << endl;return 0; }總結(jié): 鏈?zhǔn)骄幊桃⒁夥祷刂档念愋?#xff0c;如重載遞增運(yùn)算、賦值運(yùn)算都需要注意。
5.5 關(guān)系運(yùn)算符重載
作用:重載關(guān)系運(yùn)算符,可以讓兩個(gè)自定義類型對(duì)象進(jìn)行對(duì)比操作。
// 舉例 重載== bool operator==(Person &p){if(this->m_A == p.m_A && this->m_Age == p.m_Age){return true;}return false; }5.6 函數(shù)調(diào)用運(yùn)算符重載()
- 函數(shù)調(diào)用運(yùn)算符 () 也可以重載
- 由于重載后使用的方式非常像函數(shù)的調(diào)用,因此稱為仿函數(shù)
- 仿函數(shù)沒有固定寫法, 非常靈活
6、繼承
繼承是面向?qū)ο笕筇匦灾?/strong>。可以減少重復(fù)代碼
6.1 繼承的基本語(yǔ)法
語(yǔ)法:class 子類 : 繼承方式 父類
術(shù)語(yǔ): 子類又稱 派生類,父類又稱 基類
派生類中的成員包含兩大部分:
一類是從基類中繼承來的,另一類是它自身的成員
6.2 繼承方式
繼承有三種方式:
父類中的私有private內(nèi)容,子類無論哪種方式都不能訪問。
- 公共繼承 :父類中public、protected在子類的訪問權(quán)限不變
- 保護(hù)繼承 :父類中public、protected在子類中訪問權(quán)限變?yōu)閜rotected
- 私有繼承 :父類中public、protected在子類中訪問權(quán)限變?yōu)閜rivate
6.3 繼承中的對(duì)象模型
問題:從父類繼承過來的成員, 哪些屬于子類對(duì)象中?
6.4 繼承中的構(gòu)造和析構(gòu)
子類繼承父類后,當(dāng)創(chuàng)建子類對(duì)象,也會(huì)調(diào)用父類的構(gòu)造函數(shù)
問題: 父類和子類的構(gòu)造和析構(gòu)順序誰(shuí)先誰(shuí)后?
先父類構(gòu)造,后子類構(gòu)造;先析構(gòu)子類,后析構(gòu)父類,與構(gòu)造相反
// 繼承中的構(gòu)造和析構(gòu)順序 class Base { public:Base() {cout << "Base的構(gòu)造函數(shù)!" << endl;}~Base() {cout << "Base的析構(gòu)函數(shù)!" << endl;} };class Son :public Base { public:Son() {cout << "Son的構(gòu)造函數(shù)!" << endl;}~Son() {cout << "Son的析構(gòu)函數(shù)!" << endl;} };int main() {Son s1;return 0; }6.5 繼承中同名成員處理方式
問題:當(dāng)子類與父類出現(xiàn)同名的成員,如何通過子類對(duì)象,訪問到子類或父類中同名的數(shù)據(jù)呢?
- 訪問子類同名成員 直接訪問即可
- 訪問父類同名成員 需要加作用域
注意事項(xiàng):
如果子類中出現(xiàn)和父類同名的成員函數(shù),子類的同名成員會(huì)隱藏掉父類中所有同名成員函數(shù)(包含重載函數(shù))。 如果想要訪問父類中被隱藏的同名成員函數(shù),需要加 作用域。
6.6 繼承中同名靜態(tài)成員處理方式
問題:繼承中同名的靜態(tài)成員在子類對(duì)象上如何進(jìn)行訪問?
靜態(tài)成員和非靜態(tài)成員出現(xiàn)同名,處理方式一致
- 訪問子類同名成員 直接訪問即可
- 訪問父類同名成員 需要加作用域
注意:靜態(tài)成員在類內(nèi)聲明,類外初始化。
// 同名靜態(tài)成員 與 同名靜態(tài)成員函數(shù) class Base { public:static int m_A;static void func() {cout <<" Base - static void func()" << endl;} }; int Base::m_A = 100;class Son :public Base { public:static int m_A;static void func() {cout << " Son - static void func()" << endl;} }; int Son::m_A = 200;int main() {Son s;// 1、通過對(duì)象訪問cout << "Son 下 m_A = " << s.m_A << endl;cout << "Base 下 m_A = " << s.Base::m_A << endl;// 2、通過類名訪問cout << "Son 下 m_A = " << Son::m_A << endl;cout << "Base 下 m_A = " << Son::Base::m_A << endl;// Son::Base::m_A 通過類名方式訪問Son中Base作用域下的m_A// 1、通過對(duì)象訪問s.func();s.Base::func();// 2、通過類名訪問Son::func();Son::Base::func();return 0; }6.7 多繼承語(yǔ)法
C++允許一個(gè)類繼承多個(gè)語(yǔ)法
語(yǔ)法: class 子類: 繼承方式 父類1, 繼承方式 父類2 ...
多繼承可能會(huì)引發(fā)父類中有同名成員出現(xiàn),需要加作用域區(qū)分。
實(shí)際開發(fā)中不建議用多繼承。
// 多繼承 class A { public:A() {cout << "Create A !" << endl;}int m_N; };class B { public:B() {cout << "Create B !" << endl;}int m_N; };class C : public A, public B{ // 多繼承語(yǔ)法 public:C() {m_C = 100;m_D = 200;cout << "Create C !" << endl;}int m_C;int m_D; }; int main() {C c;cout << "This is A:" << c.A::m_N << endl;cout << "This is B:" << c.B::m_N << endl;return 0; }6.8 菱形繼承
菱形繼承的概念:
? 兩個(gè)派生類繼承同一個(gè)基類, 又有某個(gè)類同時(shí)繼承兩個(gè)派生類。這種繼承被稱為菱形繼承,或者鉆石繼承。
問題:
-
二義性
-
有的數(shù)據(jù)重復(fù),只需要一項(xiàng)就可以
總結(jié):利用 虛繼承 解決菱形繼承問題。術(shù)語(yǔ)理解:虛基類,虛繼承 關(guān)鍵字 virtual。
7、多態(tài)
多態(tài)是 C++ 面向?qū)ο蟮娜筇匦?#xff08;封裝、繼承、多態(tài))之一。
7.1 多態(tài)的基本概念
多態(tài)分為兩類:
- 靜態(tài)多態(tài): 函數(shù)重載和運(yùn)算符重載屬于靜態(tài)多態(tài),復(fù)用函數(shù)名
- 動(dòng)態(tài)多態(tài):派生類和虛函數(shù)實(shí)現(xiàn)運(yùn)行時(shí)多態(tài)
靜態(tài)多態(tài)和動(dòng)態(tài)多態(tài)的區(qū)別:
- 靜態(tài)多態(tài)的函數(shù)地址早綁定 – 編譯階段確定函數(shù)地址
- 動(dòng)態(tài)多態(tài)的函數(shù)地址晚綁定 – 運(yùn)行階段確定函數(shù)地址
總結(jié):
多態(tài)滿足條件:
- 有繼承關(guān)系
- 子類重寫父類中的虛函數(shù)
多態(tài)使用條件
- 父類的 指針或引用 指向 子類對(duì)象
重寫: 函數(shù)返回值類型 函數(shù)名 函數(shù)列表 完全一致稱為 重寫
7.2 虛函數(shù)(考點(diǎn))
實(shí)現(xiàn)動(dòng)態(tài)綁定的函數(shù)。
virtual 只是編譯器不要在編譯階段進(jìn)行靜態(tài)綁定。
虛函數(shù)定義:
什么函數(shù)可以是虛函數(shù):
-
一般成員函數(shù)
-
構(gòu)造函數(shù)不能是
-
析構(gòu)函數(shù)可以是
一般虛函數(shù)成員
virtual關(guān)鍵字:
派生類可以不顯式地用 virtual 關(guān)鍵字聲明虛函數(shù),這時(shí)候系統(tǒng)會(huì)根據(jù)以下規(guī)則來判斷派生類地一個(gè)函數(shù)成員是不是虛函數(shù):
7.3 多態(tài)的基本概念
虛函數(shù)指針 vfptr
虛函數(shù)表 vftable:
-
每一個(gè)多態(tài)類都有一個(gè)虛表
-
虛表中有當(dāng)前類的各個(gè)虛函數(shù)的入口地址
-
每個(gè)對(duì)象隱含有一個(gè)指向當(dāng)前類的虛表的指針
7.4 多態(tài)案例
多態(tài)案例一:計(jì)算器類
案例描述: 分別利用普通寫法和多態(tài)技術(shù),設(shè)計(jì)實(shí)現(xiàn)兩個(gè)操作數(shù)進(jìn)行運(yùn)算的計(jì)算器類
多態(tài)的優(yōu)點(diǎn):
- 代碼組織結(jié)構(gòu)清晰
- 可讀性強(qiáng)
- 利于前期和后期的擴(kuò)展以及維護(hù)
7.5 純虛函數(shù)和抽象類
在多態(tài)中,通常父類中虛函數(shù)的實(shí)現(xiàn)是毫無意義的,主要都是調(diào)用子類重寫的內(nèi)容
因此 可以將虛函數(shù)改為純虛函數(shù)
純虛函數(shù)語(yǔ)法: virtual 返回值類型 函數(shù)名 (參數(shù)列表) = 0;
當(dāng)類中有了純虛函數(shù),這個(gè)類也稱為抽象類。
抽象類的特點(diǎn):
- 無法實(shí)例化對(duì)象
- 子類必須重寫抽象類中的純虛函數(shù),否則也屬于抽象類
7.6 虛析構(gòu)和純虛析構(gòu)
多態(tài)使用時(shí), 如果子類中有屬性開辟到堆區(qū), 那么父類指針在釋放時(shí)無法調(diào)用到子類的析構(gòu)代碼。
解決方式:將父類中的析構(gòu)函數(shù)改為虛析構(gòu)或者純虛析構(gòu)
虛析構(gòu)和純虛析構(gòu)共性:
- 可以解決父類指針釋放子類對(duì)象
- 都需要具體函數(shù)實(shí)現(xiàn)
區(qū)別:
- 如果是純虛析構(gòu),該類屬于抽象類,無法實(shí)例化對(duì)象。
語(yǔ)法:
虛析構(gòu)語(yǔ)法:virtual ~類名(){}
純虛析構(gòu)語(yǔ)法: virtual ~類名 = 0; 類名::~類名(){} 純虛析構(gòu) 需要有聲明 類外寫實(shí)現(xiàn)
class Animal { public:Animal() {cout << "Animal類的構(gòu)造函數(shù)調(diào)用" << endl;}// 利用虛析構(gòu)可以解決 父類指針釋放子類對(duì)象時(shí)不干凈的問題/*virtual ~Animal() {cout << "Animal類的析構(gòu)函數(shù)調(diào)用" << endl;}*/// 純虛析構(gòu) 需要有聲明 也需要有實(shí)現(xiàn)的// 有了純虛析構(gòu)之后, 這個(gè)類也屬于抽象類virtual ~Animal() = 0;// 純虛函數(shù)virtual void speak() = 0; }; // 純虛析構(gòu)后 在外部實(shí)現(xiàn)析構(gòu)函數(shù) Animal::~Animal() {cout << "Animal類的析構(gòu)函數(shù)調(diào)用" << endl;}class Cat: public Animal { public:Cat(string name) {cout << "Cat類構(gòu)造函數(shù)調(diào)用" << endl;this->m_Name = new string(name);}~Cat(){if (this->m_Name != nullptr) {cout << "Cat類析構(gòu)函數(shù)調(diào)用" << endl;delete this->m_Name;m_Name = nullptr;}}virtual void speak() {cout << *this->m_Name <<"小貓?jiān)谡f話" << endl;}string* m_Name; };int main() {Animal* animal = new Cat("Tom");animal->speak();// 父類指針在析構(gòu)時(shí)候 不會(huì)調(diào)用子類中的析構(gòu)函數(shù),導(dǎo)致子類如果有堆區(qū)屬性,// 會(huì)出現(xiàn)內(nèi)存泄露情況delete animal;return 0; }總結(jié):
7.7 override 和 final
override:C++ 11中引入的顯示覆蓋。使用override后,編譯器會(huì)檢查基類中是否存在一虛函數(shù),與派生類中帶有聲明override的虛函數(shù),有相同的虛函數(shù)簽名(函數(shù)名 參數(shù)列表 const一致),若不存在,則返回錯(cuò)誤
final:不希望類或函數(shù)被修改,使用final聲明則該類(或函數(shù))將不能被繼承(或覆蓋)。
class Base final{}; virtual void f() final;8、文件操作
程序運(yùn)行時(shí)產(chǎn)生的數(shù)據(jù)都屬于臨時(shí)數(shù)據(jù),程序一旦運(yùn)行結(jié)束都會(huì)被釋放, 通過文件可以將 數(shù)據(jù)持久化
C++中對(duì)文件操作需要包含頭文件 #include<fstream>
文件類型分為兩種:
操作文件的三大類:
8.1 文本文件
寫文件的步驟:
文件打開方式:
| ios::in | 為讀文件而打開文件 |
| ios::out | 為寫文件而打開文件 |
| ios::ate | 初始位置:文件尾 |
| ios::app | 追加方式寫文件 |
| ios::trunc | 如果文件存在先刪除,再創(chuàng)建 |
| ios::binary | 二進(jìn)制方式 |
注意:文件打開方式可以配合使用, 利用|操作符
例如:用二進(jìn)制方式寫文件ios::binary | ios::out
#include<fstream> int main() {// 2、創(chuàng)建流對(duì)象ofstream ofs;// 3、制定打開方式ofs.open("test.txt", ios::out);// 4、寫內(nèi)容ofs << "姓名:張三" << endl;ofs << "性別:男" << endl;ofs << "年齡:18" << endl;ofs.close();return 0; }讀文件的步驟
8.2 二進(jìn)制文件
以二進(jìn)制的方式對(duì)文件進(jìn)行讀寫操作,打開方式要指定為 ios::binary 。
寫文件:
二進(jìn)制方式寫文件主要利用流對(duì)象調(diào)用成員函數(shù) write
函數(shù)原型:ostream& write(const char * buffer, int len);
參數(shù)解釋:字符指針buffer指向內(nèi)存中一段存儲(chǔ)空間。len是讀寫的字節(jié)數(shù)。
// 二進(jìn)制文件 寫文件 注意二進(jìn)制寫文件盡量不要用string class Person { public:char m_Name[64]; // 姓名int m_Age; // 年齡 };int main() {// 2、 創(chuàng)建流對(duì)象ofstream ofs("person.txt", ios::out | ios::binary); //創(chuàng)建流對(duì)象時(shí)候直接指定// 3、 打開文件// ofs.open("person.txt", ios::out| ios::binary)// 4、 寫入文件Person p = { "張三", 18 };ofs.write((const char *)&p,sizeof(Person));// 5、 關(guān)閉文件ofs.close();return 0; }總結(jié)://創(chuàng)建流對(duì)象時(shí)候直接指定
讀文件:
二進(jìn)制讀文件主要利用流對(duì)象調(diào)用成員函數(shù)read
函數(shù)原型:istream& read(char * buffer, int len);
參數(shù)解釋: buffer指向內(nèi)存中一段存儲(chǔ)空間,len是讀寫的字節(jié)數(shù)。
// 二進(jìn)制文件 讀文件 class Person { public:char m_Name[64]; // 姓名int m_Age; // 年齡 };int main() {// 2、 創(chuàng)建流對(duì)象ifstream ifs("person.txt", ios::out | ios::binary); //創(chuàng)建流對(duì)象時(shí)候直接指定// 3、 打開文件 判斷文件是否打開成功if (!ifs.is_open()) {cout << "打開成功" << endl;}// 4、 寫入文件Person p;ifs.read((char *)&p,sizeof(Person));cout << "姓名: " << p.m_Name << " 年齡:" << p.m_Age << endl;// 5、 關(guān)閉文件ifs.close();return 0; }C++泛型編程和STL庫(kù)
1、模板
1.1 模板的概念
模板就是建立通用的模具,大大提高復(fù)用性,例如ppt模板
缺點(diǎn):
- 模板不能直接使用
- 模板并不是萬(wàn)能的,不能包括所有的
1.2 函數(shù)模板
泛型編程:
- C++另一種編程思想稱為泛型編程, 主要利用的技術(shù)就是模板
- C++提供兩種模板機(jī)制: 函數(shù)模板 和 類模板
函數(shù)模板的語(yǔ)法:
函數(shù)模板的作用:建立一個(gè)通用參數(shù),其函數(shù)返回值類型和形參類型可以不具體指定,用一個(gè)虛擬的類型來代表。
語(yǔ)法:
template<typename T> 函數(shù)聲明或定義解釋:
template — 聲明創(chuàng)建模板
typename — 表明其后面的符號(hào)是一種數(shù)據(jù)類型,可以用class代替
T — 通用的數(shù)據(jù)類型,名稱可以替換,通常為大寫字母
// 什么情況下用函數(shù)模板 // 下面是兩個(gè)普通函數(shù),針對(duì)每一個(gè)數(shù)據(jù)類型都要單獨(dú)實(shí)現(xiàn)函數(shù) void swapInt(int &a, int &b) {int temp = a;a = b;b = temp; } void swapDouble(double& a, double& b) {double temp = a;a = b;b = temp; }// 聲明一個(gè)模板,告訴編譯器后面代碼緊跟著T不要報(bào)錯(cuò),T是一個(gè)通用數(shù)據(jù)類型 template <typename T> // typename class都可以 void mySwap(T& a, T& b) {T temp = a;a = b;b = temp; }int main() {int a = 10;int b = 20;// 使用函數(shù)模板// 1、自動(dòng)類型推導(dǎo)mySwap(a, b);cout << "a is " << a << " b is " << b << endl;// 2、顯示指定類型mySwap<int>(a, b);return 0; }總結(jié):
- 函數(shù)模板利用關(guān)鍵字 template
- 使用函數(shù)模板有兩種方式: 自動(dòng)類型推導(dǎo)、顯示指定類型
- 模板的目的是為了提高復(fù)用性,將類型參數(shù)化
1.3 函數(shù)模板注意事項(xiàng)
- 自動(dòng)類型推導(dǎo),必須推導(dǎo)出一致的數(shù)據(jù)類型T,才可以使用
- 模板必須要確定出T的類型,才可以使用
1.4 函數(shù)模板案例
案例描述:
- 利用函數(shù)模板封裝一個(gè)排序的函數(shù),可以對(duì)不同數(shù)據(jù)類型數(shù)組進(jìn)行排序
- 排序規(guī)則從大到小,排序算法為選擇排序
- 分別利用char 數(shù)組和int數(shù)組進(jìn)行測(cè)試
總結(jié):
函數(shù)模板如何聲明,如何在不同文件中使用?
1.5 普通函數(shù)與函數(shù)模板的區(qū)別
普通函數(shù)與函數(shù)模板的區(qū)別:
- 普通函數(shù)可以發(fā)生自動(dòng)類型轉(zhuǎn)換(隱式類型轉(zhuǎn)換)
- 函數(shù)模板調(diào)用時(shí),如果利用自動(dòng)類型推導(dǎo),不會(huì)發(fā)生隱式類型轉(zhuǎn)換
- 如果利用顯式指定類型的方式,可以發(fā)生隱式類型轉(zhuǎn)換
建議:使用顯式指定類型的方式調(diào)用函數(shù)模板,因?yàn)榭梢宰约捍_定通用類型T。
1.6 普通函數(shù)與函數(shù)模板的調(diào)用規(guī)則
調(diào)用規(guī)則:
- 如果函數(shù)模板和普通函數(shù)都可以實(shí)現(xiàn),優(yōu)先調(diào)用普通函數(shù)。
- 可以通過 空模板參數(shù)列表, 強(qiáng)制調(diào)用函數(shù)模板
- 函數(shù)模板也可以重載
- 如果函數(shù)模板可以產(chǎn)生更好的匹配,優(yōu)先調(diào)用函數(shù)模板。
總結(jié):既然提供了函數(shù)模板,最好就不要再提供普通函數(shù),否則容易出現(xiàn)二義性
1.7 模板的局限性
- 模板的通用性并不是萬(wàn)能的
解決方案:C++提供了模板的重載,可以為特定類型提供具體化的模板
class Person { public:Person(const string name, const int age): m_Name(name), m_Age(age){cout << "constructor" << endl;}string m_Name;int m_Age; };template <class T> bool myCampare(T& a, T& b) {if (a == b) {return true;}else {return false;} }// Person 不能對(duì)比 // 解決方法有兩種 // 1、重載 "==" 運(yùn)算符, 太過麻煩 // 2、利用具體化Person的版本實(shí)現(xiàn), 具體化會(huì)優(yōu)先調(diào)用 // 具體化實(shí)現(xiàn) template<> bool myCampare(Person& a, Person& b) {if (a.m_Age == b.m_Age && a.m_Name == b.m_Name) {return true;}else {return false;} }int main() {int a = 10;int b = 20;bool ret = myCampare(a, b);cout << ret << endl;Person pa("Tom", 18);Person pb("Tom", 18);bool pret = myCampare(pa, pb);cout << pret << endl;return 0; }總結(jié):
-
利用具體化的模板,可以解決自定義類型的 通用化
-
學(xué)習(xí)模板并不是為了寫模板,而是在STL庫(kù)中能夠運(yùn)用系統(tǒng)提供的模板
1.8 類模板
類模板作用:
- 建立一個(gè)通用類,類中的成員 數(shù)據(jù)類型 可以不具體指定,用一個(gè)虛擬的類型來代表。
1.9 類模板與函數(shù)模板的區(qū)別
主要有兩點(diǎn)區(qū)別:
- 類模板沒有自動(dòng)類型推導(dǎo)的使用方式,只有顯式指定類型方式
- 類模板在模板參數(shù)列表 <> 中可以有默認(rèn)參數(shù)
1.10 類模板中成員函數(shù)創(chuàng)建時(shí)機(jī)
與普通類中成員函數(shù)創(chuàng)建時(shí)機(jī)是有區(qū)別的:
- 普通類中的成員函數(shù)一開始就可以創(chuàng)建
- 類模板中的成員函數(shù)并不是在一開始就創(chuàng)建,而是在調(diào)用時(shí)才創(chuàng)建
1.11 類模板對(duì)象做函數(shù)參數(shù)
類模板實(shí)例化出對(duì)象,向函數(shù)傳參的方式有三種:
1.12 類模板與繼承
當(dāng)類模板遇到繼承時(shí),需要注意以下幾點(diǎn):
- 當(dāng)子類繼承的父類是一個(gè)類模板時(shí),子類在聲明的時(shí)候,需要指定出父類中T的類型
- 如果不指定,編譯器無法給子類分配內(nèi)存
- 如果想靈活的指定出父類中T的類型,子類也需要變?yōu)轭惸0?/li>
1.12 類模板的成員函數(shù)的類外實(shí)現(xiàn)
類外實(shí)現(xiàn):
template<class T1, class T2> class Person { public:Person(T1 name, T2 age);void showPerson();T1 m_Name;T2 m_Age; }; // 構(gòu)造函數(shù)類外實(shí)現(xiàn) template<class T1, class T2> Person<T1, T2>::Person(T1 name, T2 age) {this->m_Name = name;this->m_Age = age; } // 成員函數(shù)類外實(shí)現(xiàn) template<class T1, class T2> void Person<T1, T2>::showPerson() {cout << "姓名:" << this->m_Name << " 年齡:" << this->m_Age << endl; }int main() {Person<string, int>p1("Tom", 21);p1.showPerson();return 0; }1.13 類模板的分文件編寫
學(xué)習(xí)目標(biāo):
- 掌握類模板成員函數(shù) 分文件編寫 產(chǎn)生的問題以及解決方式
問題:
- 類模板中成員函數(shù)創(chuàng)建時(shí)機(jī)是在調(diào)用階段,導(dǎo)致分文件編寫時(shí)鏈接不到
解決:
-
解決方式1:直接包含.cpp源文件
-
解決方式2:將聲明和實(shí)現(xiàn)寫到同一個(gè)文件中,并更改后綴名為.hpp,hpp是約定的名稱,并不是強(qiáng)制
解決方式1
#include "Person.cpp"解決方式2
// Person.hpp #pragma once#include<iostream> #include <string> using namespace std;template<class T1, class T2> class Person { public:Person(T1 name, T2 age);void showPerson();T1 m_Name;T2 m_Age; };// 構(gòu)造函數(shù)類外實(shí)現(xiàn) template<class T1, class T2> Person<T1, T2>::Person(T1 name, T2 age) {this->m_Name = name;this->m_Age = age; } // 成員函數(shù)類外實(shí)現(xiàn) template<class T1, class T2> void Person<T1, T2>::showPerson() {cout << "姓名:" << this->m_Name << " 年齡:" << this->m_Age << endl; } // main.cpp #include<iostream> #include <string> using namespace std; // 第一種解決方式(很少采用),直接包含 頭文件Person.h 改成源文件 Person.cpp。 // #include"Person.cpp" // 第二種解決方式, 將.h 和.cpp內(nèi)容寫到一起,將后綴名改為.hpp文件; #include"Person.hpp"int main() {Person<string, int>p1("Tom", 21);p1.showPerson();return 0; }1.13 類模板與友元
學(xué)習(xí)目標(biāo):
- 掌握類模板配合友元函數(shù)的類內(nèi)和類外實(shí)現(xiàn)
全局函數(shù) 類內(nèi) 實(shí)現(xiàn) – 直接在類內(nèi)聲明友元即可
全局函數(shù) 類外 實(shí)現(xiàn) – 需要提前讓編譯器知道全局函數(shù)的存在
template<class T1, class T2> // Person模板類聲明 class Person; // 讓編譯器看到下面函數(shù)中的Person類 // 全局函數(shù) 類外實(shí)現(xiàn) 實(shí)現(xiàn)部分 要放在前面 讓編譯器知道 template<class T1, class T2> void printPerson2(Person<T1, T2> p) { // 全局函數(shù) 不用加Person作用域cout << "全局函數(shù) 類外實(shí)現(xiàn) /n 姓名: " << p.m_Name << " 年齡: " << p.m_Age << endl; }template<class T1, class T2> class Person { public:Person(T1 name, T2 age) {this->m_Name = name;this->m_Age = age;}// 全局函數(shù) 類內(nèi)實(shí)現(xiàn)friend void printPerson(Person<T1, T2> p) {cout << "全局函數(shù) 類內(nèi)實(shí)現(xiàn)/n 姓名: " << p.m_Name << " 年齡: " << p.m_Age << endl;}// 全局函數(shù) 類外實(shí)現(xiàn) 聲明部分// printPerson2后 加 空模板的參數(shù)列表// 如果全局函數(shù) 是類外實(shí)現(xiàn),需要讓編譯器提前知道該全局函數(shù)的實(shí)現(xiàn)friend void printPerson2<>(Person<T1, T2> p); // 普通函數(shù)的聲明private:T1 m_Name;T2 m_Age; };int main() {// 1.全局函數(shù)在類內(nèi)實(shí)現(xiàn)Person<string, int> p("Tom", 21);printPerson(p);printPerson2(p);return 0; }總結(jié):建議全局函數(shù)做類內(nèi)實(shí)現(xiàn),用法簡(jiǎn)單,而且編譯器可以直接識(shí)別。如果不將全局函數(shù)提前,可以使用全函數(shù)的聲明
1.13 案例實(shí)現(xiàn)一個(gè)通用的數(shù)組類
要求如下:
- 可以對(duì)內(nèi)置數(shù)據(jù)類型以及自定義類型的數(shù)據(jù)進(jìn)行存儲(chǔ)
- 將數(shù)組中的數(shù)據(jù)存儲(chǔ)到堆區(qū)
- 構(gòu)造函數(shù)中可以傳入數(shù)組的容量
- 提供對(duì)應(yīng)的拷貝構(gòu)造函數(shù)以及**operator=**防止淺拷貝問題
- 提供尾插法和尾刪法對(duì)數(shù)組中的數(shù)據(jù)進(jìn)行增加和刪除
- 可以通過下標(biāo)的方式訪問數(shù)組中的元素
- 可以獲取數(shù)組中當(dāng)前元素個(gè)數(shù)和數(shù)組的容量
2、STL 初識(shí)
- C++的面向?qū)ο蠛头盒途幊趟枷?#xff0c;目的就是復(fù)用性的提升
- 為了建立數(shù)據(jù)結(jié)構(gòu)和算法的一套標(biāo)準(zhǔn),誕生了STL
2.1 STL基本概念
- STL(Standard Template Library, 標(biāo)準(zhǔn)模板庫(kù) )
- STL從廣義上分為:容器(container) 算法(algorithm) 迭代器(iterator)
- 容器 和 算法 之間通過 迭代器 進(jìn)行無縫連接
- STL幾乎所有代碼都采用了模板類或者模板函數(shù)
2.2 STL六大組件
STL大體分為六大組件,分別是 容器、算法、迭代器、 仿函數(shù)、適配器(配接器)、空間配置器
2.3 STL中容器、算法、迭代器
容器:置物之所也
STL容器就是將運(yùn)用最廣泛的一些 數(shù)據(jù)結(jié)構(gòu) 實(shí)現(xiàn)出來
常用的數(shù)據(jù)結(jié)構(gòu):數(shù)組,鏈表,樹,棧,隊(duì)列,集合,映射表等
這些容器分為序列容器和關(guān)聯(lián)式容器兩種:
- 序列式容器:強(qiáng)調(diào) 值 的排序,序列式容器中的每個(gè)元素均有固定的位置
- 關(guān)聯(lián)式容器:二叉樹結(jié)構(gòu),各元素之間沒有嚴(yán)格的物理上順序關(guān)系
算法:問題之解法也
有限的步驟,解決邏輯或數(shù)學(xué)上的問題,這一門學(xué)科我們叫做算法
算法分為:質(zhì)變算法 和 非質(zhì)變算法
質(zhì)變算法:是指運(yùn)算過程中會(huì)更改區(qū)間內(nèi)元素的內(nèi)容。例如拷貝、替換、刪除等等
非質(zhì)變算法:是指運(yùn)算過程中不會(huì)更改區(qū)間內(nèi)的元素內(nèi)容,例如查找、計(jì)數(shù)、遍歷、尋找極值等等
迭代器:容器和算法之間的膠合劑
提供一種方法,使之能夠依序訪問某個(gè)容器所含的各個(gè)元素,而又無需暴露該容器內(nèi)部表示方式。
每個(gè)容器都有自己專屬迭代器。
迭代器使用非常類似與指針,初學(xué)階段我們可以先理解為指針
迭代器種類:
| 輸入迭代器 | 對(duì)數(shù)據(jù)的只讀訪問 | 只讀,支持++、==、!= |
| 輸出迭代器 | 對(duì)數(shù)據(jù)的只寫訪問 | 只寫,支持++ |
| 前向迭代器 | 讀寫操作,并能向前推進(jìn)迭代器 | 讀寫,支持++、==、!= |
| 雙向迭代器 | 讀寫操作,并能向前向和后操作 | 讀寫,支持++、– |
| 隨機(jī)訪問迭代器 | 讀寫操作,可以以跳躍的方式訪問任意數(shù)據(jù),功能最強(qiáng)的迭代器 | 讀寫,支持++、–、[n]、-n、<、<=、>、>= |
常用的容器中迭代器種類為雙向迭代器、隨機(jī)訪問迭代器。
2.4 容器算法迭代器初識(shí)
STL中最常用的容器為vector,可以理解為數(shù)組
vector存放內(nèi)置數(shù)據(jù)類型:
容器: vector
算法:for_each
迭代器:vector<int>::iterator
#include<iostream> using namespace std; #include<vector> #include<algorithm>void myPrint(int val) { // 供第三種遍歷方法的for_each使用cout << val << endl; }int main() {// 創(chuàng)建vector容器vector<int> v;// 向容器中插入數(shù)據(jù)v.push_back(10);v.push_back(20);v.push_back(30);v.push_back(40);// 通過迭代器訪問容器中的數(shù)據(jù)// 起始迭代器 指向容器中的 第一個(gè)元素vector<int>::iterator itBegin = v.begin(); // 結(jié)束迭代器 指向容器中最后一個(gè)元素的下一個(gè)位置vector<int>::iterator itEnd = v.end(); // 第一種遍歷方式while (itBegin != itEnd) {cout << *itBegin << endl; // 使用*解引用itBegin++;}// 第二種遍歷方式 常用for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {cout << *it << endl;}// 第三種遍歷方式 使用Algorithm 中 for_each 算法for_each(v.begin(), v.end(), myPrint); }vector存放自定義數(shù)據(jù)類型:
class Person { public:Person(string name, int age) {this->m_Name = name;this->m_Age = age;}string m_Name;int m_Age; };// 存放Person類型 void test1() {// 創(chuàng)建vector容器vector<Person> v;// Person類型Person p1("Tom", 16);Person p2("Tom1", 116);Person p3("Tom2", 216);Person p4("Tom3", 316);Person p5("Tom4", 416);// 向容器中插入數(shù)據(jù)v.push_back(p1);v.push_back(p2);v.push_back(p3);v.push_back(p4);v.push_back(p5);for (vector<Person>::iterator it = v.begin(); it != v.end(); it++) {// (*it) 是Person對(duì)象cout << "姓名:" << (*it).m_Name << " 年齡:" << (*it).m_Age << endl;cout << "姓名:" << it->m_Name << " 年齡:" << it->m_Age << endl;} }// 存放Person類型指針 void test2() {// 創(chuàng)建vector容器vector<Person *> v;// Person類型Person p1("Tom", 16);Person p2("Tom1", 116);Person p3("Tom2", 216);Person p4("Tom3", 316);Person p5("Tom4", 416);// 向容器中插入數(shù)據(jù)v.push_back(&p1);v.push_back(&p2);v.push_back(&p3);v.push_back(&p4);v.push_back(&p5);for (vector<Person *>::iterator it = v.begin(); it != v.end(); it++) {// *it 是指針 訪問Person中的屬性 要用 (*it)->m_Namecout << "姓名:" << (*it)->m_Name << " 年齡:" << (*it)->m_Age << endl;} } int main() {test1();test2();return 0; }vector容器嵌套容器:
容器中嵌套容器, 然后遍歷輸出
int main() {// 容器嵌套容器vector<vector<int>> v;// 創(chuàng)建小容器 v1;vector<int> v1;vector<int> v2;vector<int> v3;vector<int> v4;for (int i = 0; i < 4; i++) {v1.push_back(i+1);v2.push_back(i+2);v3.push_back(i+3);v4.push_back(i+4);}v.push_back(v1);v.push_back(v2);v.push_back(v3);v.push_back(v4);for (vector<vector<int>>::iterator it = v.begin(); it != v.end(); it++) { // 判斷條件用!= 或者 < 都可以// (*it) -- 容器 vector<int>for (vector<int>::iterator vit = (*it).begin(); vit != (*it).end(); vit++) {cout << (*vit) << "\t";}cout << endl;}return 0; }3、STL – 常用容器
常用容器有,string、vector、deque、stack、 queue、list、set/multiset、map/multimap。
3.1 String 容器
1. 基本概念:
本質(zhì):string 是 C++ 風(fēng)格的字符串, 而string本質(zhì)上是一個(gè)類
string和char*的 區(qū)別:
- char * 是一個(gè)指針
- string 是一個(gè)類,類內(nèi)部封裝了char*,管理這個(gè)字符串,是一個(gè) char * 型的的容器
特點(diǎn):
string 類內(nèi)部封裝了很多成員方法
例如: 查找find, 拷貝copy,刪除delete, 替換replace, 插入insert
string 管理 char*所分配的內(nèi)存,不用擔(dān)心復(fù)制越界和取值越界,由類內(nèi)部進(jìn)行負(fù)責(zé)
2. string構(gòu)造函數(shù)
構(gòu)造函數(shù)原型:
- string(); // 創(chuàng)建一個(gè)空的字符串 例如: string str;
- string(const char * s) // 使字符串s初始化
- string(const string & str); // 使用一個(gè)string對(duì)象初始化另一個(gè)string對(duì)象
- string(int n, char c); // 使用n個(gè)字符c初始化
3. string 賦值操作
給string字符串進(jìn)行賦值
賦值函數(shù)原型:
- string& operator=(const char *s); // char * 類型字符串 賦值 給當(dāng)前字符串
- string& operator=(const string &s); // 把字符串s賦給當(dāng)前的字符串
- string& operator=(char c); // 字符賦值給當(dāng)前的字符串
- strin& assign(const char*s); // 把字符串s賦值給當(dāng)前的字符串
- string& assign(const char *s, int n); // 把字符串s的前n個(gè)字符賦給當(dāng)前的字符串
- string & assign(const string &s); //把字符串s賦給當(dāng)前字符串
- string& assign(int n, char c); //用n個(gè)字符c賦給當(dāng)前字符串
總結(jié):string 賦值方法有很多,operator=更實(shí)用。
4. string字符串拼接
實(shí)現(xiàn)在字符串末尾拼接字符串
函數(shù)原型:
- string& operator+=(const char* str); // 重載+=運(yùn)算符
- string& operator+=(const char* c);
- string& operator+=(const string& str);
- string& append(const char *s); //把字符串s連接到當(dāng)前字符串結(jié)尾
- string& append(const char *s, int n); //把字符串s前n個(gè)字符連接到當(dāng)前字符串結(jié)尾
- string& append(const string &s); // 與第三個(gè)相同
- string& append(const string &s, int pos, int n); // 把字符串s從pos開始的n個(gè)字符連接到字符串結(jié)尾。 pos是位置,從0開始
5.字符串查找替換
查找:查找指定字符串是否存在 find
替換:在指定的位置替換字符串 replace
函數(shù)原型:
- int find(const string& str, int pos = 0) const; // 常函數(shù), 查找str第一次出現(xiàn)位置,從pos開始查找
- int find(const char* s, int pos =0) const; // 查找s第一次出現(xiàn)的位置,從pos位置開始查找
- int find(const char*s, int pos, int n) const; // 從pos位置查找s前n個(gè)字符第一次位置
- int find(const char c, int pos = 0) const; // 查找字符c第一次出現(xiàn)的位置
- int rfind(const string& str, int pos = npos) const; // 查找str 最后一次位置, 從pos開始查找
- int rfind(const char* s, int pos = npos) const; //查找s最后一次出現(xiàn)位置,從pos=npos開始查找,從右往左數(shù)npos個(gè)元素,查找是正向的
- int rfind(const char* s, int pos, int n) const;// 從pos位置查找s的前n個(gè)字符最后一次位置
- int rfind(const char c, int pos=0) const; // 查找字符c最后一次出現(xiàn)位置
- string& replace(int pos, int n, const string& str); // 替換從pos開始n個(gè)字符為字符串str
- string& replace(int pos, int n, const char*s);//替換從pos開始的n個(gè)字符為字符串s
6. 字符串比較
字符串之間的比較,主要用于判斷兩個(gè)字符串是否相等
- 字符串按字符的ASCII碼進(jìn)行對(duì)比。ASCII碼大小,a<z
= 返回 0
> 返回 1
< 返回-1
函數(shù)原型:
- int compare(const string& s) const; //與字符串s比較
- int compare(const char* s) const; // 與字符串s比較
7. 字符存取
string中單字符存取方式有兩種
- char& operator[](int n); //通過[]方式取字符
- char& at(int n); // 通過at方法獲取字符
8. 字符串插入和刪除
對(duì)string字符串進(jìn)行插入和刪除字符操作
- string& insert(int pos, const char* s); //插入字符串
- string& insert(int pos, const string& str); //插入字符串
- string& insert(int pos, int n, char c); // 在指定位置插入n個(gè)字符c
- string& erase(int pos, int n = npos); //刪除從pos開始的n個(gè)字符
9. 子串獲取
從字符串中獲取想要的子串
- 從字符串中獲取想要的子串
函數(shù)原型:
- string substr(int pos=0, int n = pos) const; // 返回由pos開始的n個(gè)字符組成的字符串
總結(jié):可以跟其他查找成員函數(shù)配合,如find等來截取特定子串
3.2 vector容器
1. Vector基本概念
功能:
- vector數(shù)據(jù)結(jié)構(gòu)和數(shù)組非常相似,也稱為單端數(shù)組
與普通數(shù)組的區(qū)別:
- 不同之處在于數(shù)組是靜態(tài)空間,而vector可以動(dòng)態(tài)擴(kuò)展
動(dòng)態(tài)擴(kuò)展:
- 并不是在原空間之后續(xù)接新空間,而是找更大的連續(xù)內(nèi)存空間,然后將原數(shù)據(jù)拷貝新空間,釋放原空間
vector容器的迭代器是支持隨機(jī)訪問的迭代器。 begin()、end()、rbegin()、 rend()
函數(shù)原型:
- vector<T> v; //采用模板實(shí)現(xiàn)類實(shí)現(xiàn),默認(rèn)構(gòu)造函數(shù)
- vector(v.begin(), v.end()); // 將v[begin(), end()) 左閉右開區(qū)間中的元素拷貝給本身,因?yàn)閑nd()是指向最后一個(gè)元素的下一個(gè)位置
- vector(n, elem); // 構(gòu)造函數(shù)將n個(gè)elem拷貝給本身
- vector(const vector &vec); // 拷貝構(gòu)造函數(shù)
2.vector的賦值操作
函數(shù)原型:
- vector& operator=(const vector &vec); // 重載等號(hào)操作符
- assign(beg, end); // 將[beg, end)區(qū)間中的數(shù)據(jù)拷貝賦值給本身
- assign(n, elem); // 將n個(gè)elem拷貝賦值給本身
3.vector容量和大小
對(duì)vector容器的容量和大小的操作
函數(shù)原型:
- empty(); // 判斷容器是否為空
- capacity(); //容器的容量
- size(); // 返回容器中元素的個(gè)數(shù)
- resize(int num); // 重新指定容器的長(zhǎng)度為num, 若容器邊長(zhǎng),則以默認(rèn)值填充新位置, 如果容器變短,則末尾超出容器長(zhǎng)度的元素被刪除
- resize(int num, elem); // 重新指定容器的長(zhǎng)度為num, 若容器邊長(zhǎng), 則以elem值填充新位置,如果容器變短,則末尾超出容器長(zhǎng)度的元素被刪除
4. vector插入與刪除
函數(shù)原型:
- push_back(elem) //尾部插入元素elem
- pop_back() // 刪除最后一個(gè)元素
- insert(const_interator pos, elem) //迭代器指向位置pos插入元素elem
- insert(const_interator pos, int count, elem) //迭代器指向位置pos插入count個(gè)元素elem
- erase(const_iterator pos) // 刪除迭代器指向的元素
- erase(const_iterator start, const_iterator end) // 刪除迭代器從start到end之間的元素
- clear();
5. vector數(shù)據(jù)存取
對(duì)vector中數(shù)據(jù)的存取操作
函數(shù)原型:
- at(int idx) //返回索引 idx 所指的數(shù)據(jù)
- operator[] // 返回索引 idx 所指的數(shù)據(jù)
- front() // 返回容器中第一個(gè)數(shù)據(jù)元素
- back() // 返回容器中最后一個(gè)數(shù)據(jù)元素
6. vector互換容器
實(shí)現(xiàn)兩個(gè)容器內(nèi)元素進(jìn)行互換
函數(shù)原型:
- swap(vec); // 將vec中元素與本身的元素互換
總結(jié):可以利用swap收縮內(nèi)存
7. 預(yù)留空間
功能:減少vector在動(dòng)態(tài)擴(kuò)展容量時(shí)的擴(kuò)展次數(shù)
函數(shù)原型:reserve(int len); //容器預(yù)留len個(gè)元素長(zhǎng)度,預(yù)留位置不初始化,元素不可訪問。
注意是reserve 預(yù)留 不是 reverse 反轉(zhuǎn)
vector<int> v1; v1.reserve(100000); int* p = nullptr; int num = 0; cout << v1.capacity() << endl; // 100000 for (int i = 0; i < 100000; i++) {v1.push_back(i);if (p != &v1[0]) {p = &v1[0];num++;} } cout << num << endl;3.3 deque容器
1. 基本概念
功能:
雙端數(shù)組,可以對(duì) 頭端和尾端 進(jìn)行插入刪除操作
deque與vector區(qū)別:
- vector 對(duì)于頭部插入刪除效率低,數(shù)據(jù)量越大,效率越低
- deque相對(duì)而言,對(duì)頭部插入刪除速度會(huì)比vector快
- vector訪問元素時(shí)的速度會(huì)比deque快,這和兩者內(nèi)部實(shí)現(xiàn)相關(guān)
deque內(nèi)部工作原理:
deque內(nèi)部有個(gè)中控器,維護(hù)每段緩沖區(qū)的內(nèi)容,緩沖區(qū)存放真實(shí)數(shù)據(jù)。
中控器維護(hù)的是每個(gè)緩沖區(qū)的地址,使得使用deque時(shí) 像 一片連續(xù)的內(nèi)存空間
(原理才想,鏈表,鏈接接多個(gè)成塊的內(nèi)存空間)
- deque容器的迭代器也是支持隨機(jī)訪問
2. deque賦值操作
函數(shù)原型:
- deque& operator=(const deque & deq) //重載等號(hào)操作符
- assign(beg, end) // 將[beg, end)區(qū)間的數(shù)據(jù)拷貝賦值給本身
- assign(n, elem) // 將n個(gè)elem拷貝賦值給本身
3.deque大小操作
對(duì)deque容器的大小進(jìn)行操作
函數(shù)原型:
- deque.empty()
- deque.size()
- deque.resize(num)
- deque.resize(num, elem)
注意: deque沒有容量capacity的成員
4. deque插入和刪除
函數(shù)原型:
兩端插入:
- push_back(elem)
- push_front(elem) // 從容器頭部插入一個(gè)數(shù)據(jù) , 注意次序
- pop_back(elem)
- pop_front(elem)
指定位置操作:
- insert(pos, elem)
- insert(pos, n, elem)
- insert(pos, beg, end)
- clear()
- erase(beg, end)
- erase(pos)
5. deque數(shù)據(jù)存取
對(duì)deque中數(shù)據(jù)的存取操作
函數(shù)原型:
- at(int idx)
- operator[]
- front()
- back();
6. deque排序
算法:
- sort(iterator brg, iterator end);// 對(duì)beg到end區(qū)間內(nèi)元素進(jìn)行排序
- 對(duì)于支持隨機(jī)訪問的迭代器容器,都可以利用sort算法直接對(duì)其進(jìn)行排序
3.4 STL評(píng)委打分案例
案例描述:
有五名選手,選手ABCDE,10個(gè)評(píng)委分別對(duì)每一名選手打分,去掉最高分和最低分,取平均分。
實(shí)現(xiàn)步驟:
3.5 stack容器 棧容器
1. 基本概念
stack 是一種先進(jìn)后出(FILO)的數(shù)據(jù)結(jié)構(gòu),它只有一個(gè)出口。
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-JzSXhZgy-1599351751836)(C:\Users\ypw\AppData\Roaming\Typora\typora-user-images\image-20200904204744032.png)]
棧中只有頂端的元素才可以被外界使用,因此棧不允許有遍歷行為。
2. stack常用接口
構(gòu)造函數(shù):
- stack<T> stk
- stack(const stack &stk);
賦值操作:
- stack& operator=(const stack& stk)
數(shù)據(jù)存取:
- push(elem)
- pop() // 從棧頂移除第一個(gè)元素
- top() // 返回棧頂元素
大小操作:
- empty()
- size() // 返回棧的大小
3.6 queue 容器 隊(duì)列
Queue是一種先進(jìn)先出的數(shù)據(jù)結(jié)構(gòu)
隊(duì)列容器只有隊(duì)頭和隊(duì)尾才可以被外界使用,因此隊(duì)列不允許有遍歷行為。
常用接口:
構(gòu)造函數(shù):
- queue<T> q
- queue(const queue& q)
賦值操作:
- queue& operator=(const queue &q)
數(shù)據(jù)存取:
- push(elem)
- pop()
- back() 返回最后一個(gè)元素
- front()
大小操作:
- empty()
- size()
3.7 list容器 鏈表
1. 基本概念
功能:將數(shù)據(jù)進(jìn)行鏈?zhǔn)酱鎯?chǔ),可以在任意位置上進(jìn)行快速插入或刪除元素
鏈表(list):是一種物理存儲(chǔ)單元上非連續(xù)的存儲(chǔ)結(jié)構(gòu),數(shù)據(jù)元素的邏輯順序是通過鏈表中的指針鏈接實(shí)現(xiàn)的
鏈表的組成: 鏈表由一系列的 結(jié)點(diǎn) 組成
結(jié)點(diǎn): 一個(gè)是存儲(chǔ)數(shù)據(jù)元素的數(shù)據(jù)域,另一個(gè)是存儲(chǔ)下一個(gè)結(jié)點(diǎn)地址的指針域
STL中的鏈表是一個(gè) 雙向循環(huán)鏈表
優(yōu)點(diǎn):
- 采用動(dòng)態(tài)存儲(chǔ)分配,不會(huì)造成內(nèi)存浪費(fèi)和溢出
- 鏈表執(zhí)行插入和刪除操作十分方便,修改指針即可,不需要移動(dòng)大量元素
缺點(diǎn):
- 容器遍歷速度,沒有數(shù)組快
- 占用空間比數(shù)組大,因?yàn)椴粌H包含數(shù)據(jù)域還包含指針域 指針4位
性質(zhì): 插入操作和刪除操作都不會(huì)造成原有l(wèi)ist迭代器失效, 這在vector是不成立的。
2. 構(gòu)造函數(shù)
- list<T> l;
- list(beg, end)
- list(n, elem)
- list(const list &l)
3. list賦值和交換
函數(shù)原型:
- assign(beg, end)
- assign(n, elem)
- list& operator=(const list &l);
- l.swap(l2)
4. 大小操作
函數(shù)原型:
- size()
- empty()
- resize(num);
- resize(num, elem)
5. 插入和刪除操作
函數(shù)原型:
- push_back(elem)
- pop_back()
- push_front()
- pop_front()
- insert(iterator_pos, elem)
- insert(iterator_pos, n, elem)
- insert(iterator_pos, beg, end)
- clear()
- erase(beg, end)
- erase(iterator_pos)
- remove(elem) // 刪除容器中所有與elem值匹配的元素
6. 數(shù)據(jù)存取
函數(shù)原型:
- front()
- back()
list本質(zhì)是鏈表,不是用連續(xù)線性空間存儲(chǔ)數(shù)據(jù),迭代器也不支持隨機(jī)訪問
it++; it--; // 支持雙向 it = it + 1 // 錯(cuò)誤, 不支持隨機(jī)訪問7. 反轉(zhuǎn)和排序
函數(shù)原型:
- reverse() // 反轉(zhuǎn)列表
- sort(); // 算法鏈表排序, 成員函數(shù),而不是全局函數(shù)
8. 排序案例
案例描述:將Person自定義數(shù)據(jù)類型進(jìn)行排序,Person中屬性有姓名、年齡、身高
排序規(guī)則: 按照年齡進(jìn)行升序,如果年齡相同按照身高進(jìn)行降序
void creatList(list<Person>& L) {Person p1("A", 12, 175);Person p2("B", 10, 175);Person p3("C", 17, 175);Person p4("D", 11, 175);Person p5("A", 12, 176);Person p6("A", 11, 185);L.push_back(p1);L.push_back(p2);L.push_back(p3);L.push_back(p4);L.push_back(p5);L.push_back(p6);for (list<Person>::iterator it = L.begin(); it != L.end(); it++) {cout << "姓名:" << it->m_Name << "年齡: " << it->m_Age << " 身高:" << it->m_Height << endl;} } bool comparePerson(Person& p1, Person& p2) {if (p1.m_Name == p2.m_Name) {if (p1.m_Age == p2.m_Age) {return p1.m_Height > p2.m_Height;}else {return p1.m_Age > p2.m_Age;}}return p1.m_Name > p2.m_Name; } int main() {list<Person> L;creatList(L);L.sort(comparePerson); // 指定排序規(guī)則for (list<Person>::iterator it = L.begin(); it != L.end(); it++) {cout << "姓名:" << it->m_Name << "年齡: " << it->m_Age << " 身高:" << it->m_Height << endl;}return 0; }總結(jié):使用自定義類型時(shí),要給sort指定排序規(guī)則
3.8 set/multiset 容器 集合容器
1. set/multiset基本概念
簡(jiǎn)介:所有元素都會(huì)在插入時(shí) 自動(dòng)被排序
本質(zhì):
- set/multiset 屬于 關(guān)聯(lián)式容器, 底層結(jié)構(gòu)是用 二叉樹 實(shí)現(xiàn)
set 和 multiset 的區(qū)別:
- set不允許容器中有重復(fù)的元素
- multiset允許容器中有重復(fù)的元素
2. 構(gòu)造和賦值
構(gòu)造:
- set<T> st;
- set(const set &st)
賦值:
- set& operator=(const set &st)
插入:
- insert(elem)
3. set 大小和交換
- size()
- empty()
- swap(st)
4. 插入和刪除
- insert(elem);
- clear();
- erase(pos)
- erase(beg, end)
- erase(elem)
5. 查找和統(tǒng)計(jì)
- 對(duì)set容器進(jìn)行查找數(shù)據(jù)以及統(tǒng)計(jì)數(shù)據(jù)
函數(shù)原型:
- find(key) // 查找key,若存在返回該key的元素迭代器,若不存在,返回set.end()
- count(key) // 統(tǒng)計(jì)key的個(gè)數(shù)
6. set和multiset
都包含在#include<set>頭文件中
區(qū)別:
- set不可以插入重復(fù)數(shù)據(jù), 而multiset可以
- set插入數(shù)據(jù)的同時(shí)會(huì) 返回插入結(jié)果,表示插入是否成功
- multiset不會(huì)檢測(cè)數(shù)據(jù),因此可以插入重復(fù)數(shù)據(jù)
7. pair 對(duì)組創(chuàng)建
- 成對(duì)出現(xiàn)的數(shù)據(jù),利用對(duì)組可以返回兩個(gè)數(shù)據(jù)
兩種創(chuàng)建方式:
- pair<type, type> p (value1, value2)
- pair<type, type> p = make_pair(value1, value3)
8. 改變set的排序規(guī)則
利用 仿函數(shù),可以改變排序規(guī)則。
仿函數(shù): 重載了函數(shù)調(diào)用運(yùn)算符(), 本質(zhì)上是一個(gè)類型
// 內(nèi)置類型指定排序規(guī)則 class MyCompare { public:bool operator()(int v1, int v2) const { // 注意vs2019 重載()要加const return v1 > v2;} };int main() {// 指定排序規(guī)則 在插入數(shù)據(jù)之前set<int, MyCompare>s2;s2.insert(10);s2.insert(20);s2.insert(0);s2.insert(50);s2.insert(14);//printSet(s2);return 0; } // 自定義數(shù)據(jù)類型指定排序規(guī)則 class Person { public:Person(string name, int age) {this->m_Name = name;this->m_Age = age;}string m_Name;int m_Age; }; class MyCompare { public:bool operator()(Person p1, Person p2) { // 注意vs2019 重載()要加const return p1.m_Age > p2.m_Age;} };int main() {// 自定義數(shù)據(jù)類型 利用仿函數(shù)都會(huì)指定排序規(guī)則set<Person, MyCompare> s1;// 創(chuàng)建Person對(duì)象Person p1("劉備", 42);Person p2("司馬", 54);Person p3("曹操", 48);Person p4("孫權(quán)", 21);s1.insert(p1);s1.insert(p2);s1.insert(p3);s1.insert(p4);return 0; } // 該代碼g++編譯沒有問題, vs2019報(bào)錯(cuò)3.9 map/multimap 容器 高性能 高效率
1. map的基本概念
- map中所有元素都是pair
- pair中第一個(gè)元素為key(鍵值),起到索引作用, 第二個(gè)元素為value(實(shí)值)
- 所有元素都會(huì)根據(jù) 元素的鍵值 自動(dòng)排序
本質(zhì):
- map/multimap屬于關(guān)聯(lián)式容器,底層結(jié)構(gòu)是用二叉樹實(shí)現(xiàn)
優(yōu)點(diǎn):
- 可以根據(jù)key值快速找到value值
map和multimap區(qū)別:
- map不允許容器有重復(fù)key值元素
- multimap允許容器中有重復(fù)key值元素
2. map構(gòu)造和賦值操作
函數(shù)原型:
構(gòu)造:
- map<T1, T2> mp; // map默認(rèn)構(gòu)造函數(shù)
- map(const map &mp);
賦值:
- map& operator=(const map &mp)
3. map的容器大小 和交換
函數(shù)原型:
- size()
- empty()
- swap(mp)
4. map的插入和刪除
函數(shù)原型:
- insert(elem)
- clear()
- erase(pos)
- erase(pos, beg, end)
- erase(key)
5.map查找和統(tǒng)計(jì)
函數(shù)原型:
- find(key); // 查找key是否存在, 不存在返回map.end()迭代器
- count(key);
6. map排序
利用仿函數(shù),可以改變排序規(guī)則
7. 案例
4、函數(shù)對(duì)象
4.1 概念:
- 重載函數(shù)調(diào)用操作符()的類,其對(duì)象常稱為函數(shù)對(duì)象
- 函數(shù)對(duì)象使用重載的() 時(shí),行為類似函數(shù)調(diào)用,也叫 仿函數(shù)
本質(zhì):
函數(shù)對(duì)象(仿函數(shù))是一個(gè)類,不是一個(gè)函數(shù)。
4.2 函數(shù)對(duì)象使用
特點(diǎn):
- 函數(shù)對(duì)象在使用時(shí),可以像普通函數(shù)那樣調(diào)用,可有參數(shù),可有返回值
- 函數(shù)對(duì)象超出普通函數(shù)的概念,函數(shù)對(duì)象可以有自己的狀態(tài)
- 函數(shù)對(duì)象可以作為參數(shù)傳遞
4.3 謂詞 Pred
概念:
- 返回 bool類型 的仿函數(shù)稱為謂詞
- 如果operator() 接受 一個(gè)參數(shù),那么叫做 一元謂詞
- 如果operator() 接受兩個(gè)參數(shù),那么叫做 二元謂詞
4.4 內(nèi)聯(lián)函數(shù)對(duì)象 functional
概念: STL內(nèi)建了一些函數(shù)對(duì)象
分類:
- 算術(shù)仿函數(shù)
- 關(guān)系仿函數(shù)
- 邏輯仿函數(shù)
用法:
- 這些仿函數(shù)所產(chǎn)生的對(duì)象,用法和一般函數(shù)完全相同
- 使用內(nèi)建函數(shù)對(duì)象,需要引入頭文件#include<functional>
算術(shù)仿函數(shù) :
-
實(shí)現(xiàn)四則運(yùn)算
-
其中negate是一元運(yùn)算
仿函數(shù)原型:
- template<class T> T plus<T> // 加法仿函數(shù) 注意只需要一個(gè)模板類型參數(shù),兩個(gè)會(huì)報(bào)錯(cuò)
- template<class T> T minus<T> // 減法仿函數(shù)
- template<class T> T multiplies<T> // 乘法仿函數(shù)
- template<class T> T divides<T> // 除法仿函數(shù)
- template<class T> T modules<T> // 取模仿函數(shù)
- template<class T> T negate<T> // 取反仿函數(shù), 一元運(yùn)算
關(guān)系仿函數(shù)
仿函數(shù)原型:
- template<class T> bool equal_to<T>
- template<class T> bool not_equal_to<T>
- template<class T> bool greater<T>
- template<class T> bool greater_equal<T>
- template<class T> bool less<T>
- template<class T> bool less_euqal<T>
邏輯仿函數(shù)
仿函數(shù)原型:
- template<class T> bool logical_and<T>
- template<class T> bool logical_or<T>
- template<class T> bool logical_not<T>
5、常用算法
概述:
-
算法主要由頭文件<algorithm> <functional> <numeric>
-
<algorithm>是STL頭文件中最大的一個(gè),范圍涉及到比較、交換、查找、遍歷操作、復(fù)制、修改等
-
<numeric> 體積很小,只包括幾個(gè)在序列上進(jìn)行簡(jiǎn)單數(shù)學(xué)運(yùn)算的模板函數(shù)
-
<functional> 定義了一些模板類,用以聲明函數(shù)對(duì)象
5.1 常用的遍歷算法
算法簡(jiǎn)介:
-
for_each() // 遍歷容器
-
transform // 搬運(yùn)容器到另一個(gè)容器
函數(shù)原型:
for_each(iterator beg, iterator end, _func) 第四個(gè)參數(shù)是普通函數(shù)或者仿函數(shù),提供了一種伴隨操作的方法
// for_each(beg, end, 普通函數(shù)或者仿函數(shù)) #include<algorithm>void printVector(const int &val) {cout << val << " "; }class PrintVector { public:void operator()(int val) {cout << val << " ";} };int main(){vector<int> v;for (int i = 0; i < 10; i++) {v.push_back(rand()%21+10);}for_each(v.begin(), v.end(), printVector); // 注意這里是調(diào)用函數(shù) 因此只寫函數(shù)名代表函數(shù)地址cout << endl;for_each(v.begin(), v.end(), PrintVector()); // 注意這里是仿函數(shù)cout << endl;return 0; }函數(shù)原型:
transform(iterator beg1, iterator end1, iterator beg2, _func) 第四個(gè)參數(shù)函數(shù)或者仿函數(shù)
// transform class Transform{ public: int operator()(int val) {return val;} };int main(){vector<int> v;for (int i = 0; i < 10; i++) {v.push_back(rand()%21+10);}vector<int> target;target.resize(v.size()); // 目標(biāo)容器要提前開辟空間, 否則會(huì)報(bào)錯(cuò)transform(v.begin(), v.end(), target.begin(), Transform());return 0; }5.2 常用的查找算法
- find // 查找元素
- find_if // 按條件查找元素
- adjacent_find // 查找相鄰的重復(fù)元素
- binary_search // 二分查找
- count // 統(tǒng)計(jì)元素個(gè)數(shù)
- count_if // 按條件統(tǒng)計(jì)元素個(gè)數(shù)
1. find
函數(shù)原型:
- find(iterator beg, iterator end, value) // 按條件查找元素,找到返回指定位置迭代器, 找不到返回結(jié)束迭代器位置
2. find_if
函數(shù)原型:
find_if(beg, end, _Pred)
3. adjacent_find
- 查找相鄰重復(fù)元素
函數(shù)原型:
- adjacent_find(iterator beg, iterator end); // 返回相鄰元素的第一個(gè)元素的迭代器
4. binary_search
- 查找指定元素是否存在,返回值是bool類型,不是迭代器。效率較高
- 必須在 有序 序列中使用,無序序列中不可用
函數(shù)原型:
-
bool binary_search(iterator beg, iterator end, value)
返回true或者false
5. count
統(tǒng)計(jì)元素個(gè)數(shù)
函數(shù)原型:
-
count(iterator beg, iterator end, value)
返回統(tǒng)計(jì)元素出現(xiàn)的次數(shù)
6. count_if
按條件統(tǒng)計(jì)元素個(gè)數(shù)
函數(shù)原型:
-
count(iterator beg, iterator end, _Pred)
返回統(tǒng)計(jì)元素出現(xiàn)的次數(shù)
5.3 常用的排序算法
算法簡(jiǎn)介:
- sort() // 對(duì)容器內(nèi)元素進(jìn)行排序
- random_shuffle // 洗牌, 指定范圍內(nèi)的元素隨機(jī)調(diào)整次序
- merge // 容器元素合并,并存儲(chǔ)到另一個(gè)容器中
- reverse // 反轉(zhuǎn)指定范圍的元素
1. sort
對(duì)容器內(nèi)元素進(jìn)行排序
函數(shù)原型:
- sort(iterator beg, iterator end, _Pred)
2. random_shuffle
洗牌 指定范圍內(nèi)元素隨機(jī)排序
函數(shù)原型:
-
random_shuffle(iterator beg, iterator end)
可以使用隨機(jī)種子srand((unsigned int)time(nullptr));
3. merge
兩個(gè)有序序列合并成一個(gè)有序序列,并存儲(chǔ)到另一個(gè)容器,
函數(shù)原型:
- merge(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest_beg)
4. reverse
對(duì)容器內(nèi)元素進(jìn)行反轉(zhuǎn)
函數(shù)原型:
-
reverse(iterator beg, iterator end);
反轉(zhuǎn)指定范圍的元素
5.4 常用的拷貝和替換算法
算法簡(jiǎn)介:
- copy //容器內(nèi)指定范圍的元素拷貝到另一個(gè)容器
- replace ://容器內(nèi)指定舊元素,替換為新元素
- replace_if//容器內(nèi)指定范圍的舊元素,滿足條件的替換為新元素
- swap // 互換兩個(gè)容器的元素
1. copy 可以用賦值=來替代
函數(shù)原型:
-
copy(iterator beg, iterator end, iterator dest)
copy(v1.begin(), v1.end(), v2.begin()); // 目標(biāo)容器要提前開辟空間
2. replace
函數(shù)原型:
- replace(iterator beg, iterator end, oldvalue, newvalue)
3. replace_if
按條件替換,區(qū)間內(nèi)滿足條件的替換為新元素
函數(shù)原型:
- replace_if(iterator beg, iterator end, _Pred, newvalue)
利用仿函數(shù)可以靈活篩選滿足條件
4. swap
互換兩個(gè)相同類型容器中的元素
函數(shù)原型:
- swap(container c1, container c2);
5.5 常用的算術(shù)生成算法
頭文件#include <numeric>
算法簡(jiǎn)介:
- acccumulate // 計(jì)算容器元素累計(jì)和
- fill // 向容器中添加元素
1. accumulate
計(jì)算區(qū)間內(nèi)元素累計(jì)總和
函數(shù)原型:
-
accumulate(iterator beg, iterator end, value)
value : 起始的累加值
2. fill
向容器中填充指定的元素
函數(shù)原型:
-
fill(iterator beg, iterator end, value)
value 填充值
5.6 常用的集合算法
算法簡(jiǎn)介:
- set_intersection // 求兩個(gè)容器的交集
- set_union // 求兩個(gè)容器的并集
- set_difference //求兩個(gè)容器的差集
1. set_intersection
求兩個(gè)容器的交集
-
set_intersection(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest)
交集放在目標(biāo)容器中,目標(biāo)容器需要提前開辟空間,因?yàn)槭墙患∽钚〉?/p>
注意,該算法會(huì)返回交集末尾的迭代器。
2. set_union
求兩個(gè)容器的并集, 要求兩個(gè)集合必須是有序序列
-
set_intersection(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest)
并集放在目標(biāo)容器中,目標(biāo)容器需要提前開辟空間,因?yàn)槭遣⒓?#xff0c;所以取兩個(gè)容器之和
同樣要注意,要利用返回的迭代器來作為并集末尾的迭代器
3. set_difference
求兩個(gè)集合的差集, 兩個(gè)集合必須是有序序列
理解: v1, v2 v1中 不屬于v1,v2 交集 的元素; v2中 不屬于v1,v2 交集 的元素; 因此注意前后順序
-
set_intersection(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest)
交集放在目標(biāo)容器中,目標(biāo)容器需要提前開辟空間,取兩個(gè)中最大的容器大小(個(gè)人感覺取前面的容器大小就行)
同樣要注意,要利用返回的迭代器來作為差集末尾的迭代器
枚舉類
C++ 其他知識(shí)點(diǎn)
1、cin
注意類型匹配
int a; cin >> a; // 如果鍵盤輸入的是字母, 則a=0。2、隨機(jī)數(shù)
// 隨機(jī)數(shù)種子 #include <ctime> srand((unsigned int)time(NULL)); rand()3、system
system("pause"); // 請(qǐng)按任意鍵繼續(xù) system("cls"); // 清屏操作4、彩色字體輸出
cout << "\033[1;33m" << "輸出內(nèi)容" << "\033[0m" << endl;5、C++編譯
使用g++命令編譯c++文件而不是gcc
總結(jié)
- 上一篇: 1929年泡沫破裂: 又一次“剪羊毛”行
- 下一篇: linux mysql5.7版本升级,l