0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

C++基礎(chǔ)知識(shí)

jf_96884364 ? 來(lái)源:jf_96884364 ? 作者:jf_96884364 ? 2023-01-12 11:00 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

1 static關(guān)鍵字

加了 static 關(guān)鍵字的全局變量只能在本文件中使用。

static 定義的靜態(tài)局部變量分配在數(shù)據(jù)段上,普通的局部變量分配在棧上,會(huì)因?yàn)楹瘮?shù)棧幀的釋放而被釋放掉。

1.1 全局靜態(tài)變量

在全局變量前加上關(guān)鍵字 static,全局變量就定義成一個(gè) 全局靜態(tài)變量 。

內(nèi)存中的位置: 靜態(tài)存儲(chǔ)區(qū) ,在整個(gè)程序運(yùn)行期間一直存在。

初始化:未經(jīng)初始化的全局靜態(tài)變量會(huì)被 自動(dòng)初始化為 0 (自動(dòng)對(duì)象的值是任意的,除非他被顯式初始化);

作用域:全局靜態(tài)變量在聲明 僅在本文件可見(jiàn) ,他的文件之外是不可見(jiàn)的,準(zhǔn)確地說(shuō)是從定義之處開(kāi)始,到文件結(jié)尾。

1.2 局部靜態(tài)變量

在局部變量之前加上關(guān)鍵字 static,局部變量就成為一個(gè) 局部靜態(tài)變量

內(nèi)存中的位置: 靜態(tài)存儲(chǔ)區(qū) ,在整個(gè)程序運(yùn)行期間一直存在。

初始化:未經(jīng)初始化的全局靜態(tài)變量會(huì)被自動(dòng)初始化為 0(自動(dòng)對(duì)象的值是任意的,除非他被顯式初始化);

作用域:作用域仍為局部作用域,當(dāng)定義它的函數(shù)或者語(yǔ)句塊結(jié)束的時(shí)候,作用域結(jié)束。但是當(dāng)局部靜態(tài)變量離開(kāi)作用域后,并沒(méi)有銷(xiāo)毀,而是 仍然駐留在內(nèi)存當(dāng)中 ,只不過(guò)我們不能再對(duì)它進(jìn)行訪問(wèn),直到該函數(shù)再次被調(diào)用,并且值不變;

1.3 靜態(tài)函數(shù)

在函數(shù)返回類(lèi)型前加 static,函數(shù)就定義為 靜態(tài)函數(shù) 。函數(shù)的定義和聲明在默認(rèn)情況下都是 extern 的,但靜態(tài)函數(shù) 僅在本文件可見(jiàn) ,不能被其他文件所用。

函數(shù)的實(shí)現(xiàn)使用 static 修飾,那么這個(gè)函數(shù)只可在本 cpp 內(nèi)使用,不會(huì)同其他 cpp 中的同名函數(shù)引起沖突;

warning:在 頭文件中聲明非static 的全局函數(shù) ,在 cpp 內(nèi)聲明static 的全局函數(shù) ,如果你要在多個(gè) cpp 中復(fù)用該函數(shù),就把它的聲明提到頭文件里去,否則 cpp 內(nèi)部聲明需加上 static 修飾;

1.4 類(lèi)的靜態(tài)成員

對(duì)一個(gè)類(lèi)中成員變量和成員函數(shù)來(lái)說(shuō),加了 static 關(guān)鍵字,則此變量/函數(shù)就沒(méi)有 this指針了,必須通過(guò) 類(lèi)名訪問(wèn) 。

在類(lèi)中,靜態(tài)成員可以實(shí)現(xiàn)多個(gè)對(duì)象之間的數(shù)據(jù)共享,并且使用靜態(tài)數(shù)據(jù)成員還不會(huì)破壞隱藏的原則,即保證了安全性。因此, 靜態(tài)成員是類(lèi)的所有對(duì)象***享的成員 ,而不是某個(gè)對(duì)象的成員。對(duì)多個(gè)對(duì)象來(lái)說(shuō),靜態(tài)數(shù)據(jù)成員 只存儲(chǔ)一處 ,供所有對(duì)象共用。

1.5 類(lèi)的靜態(tài)函數(shù)

靜態(tài)成員函數(shù)和靜態(tài)數(shù)據(jù)成員一樣,它們都屬于 類(lèi)的靜態(tài)成員 ,它們都不是對(duì)象成員。因此,對(duì)靜態(tài)成員的引用不需要用對(duì)象名。在 靜態(tài)成員函數(shù)的實(shí)現(xiàn)中不能直接引用類(lèi)中說(shuō)明的非靜態(tài)成員 ,可以引用類(lèi)中說(shuō)明的靜態(tài)成員(這點(diǎn)非常重要)。如果靜態(tài)成員函數(shù)中要引用非靜態(tài)成員時(shí),可通過(guò)對(duì)象來(lái)引用。從中可看出,調(diào)用靜態(tài)成員函數(shù)使用如下格式:<類(lèi)名>::<靜態(tài)成員函數(shù)名>(<參數(shù)表>);

2 C++和C的區(qū)別

2.1 設(shè)計(jì)思想上

C++是面向?qū)ο?/strong>的語(yǔ)言,而 C 是面向過(guò)程的結(jié)構(gòu)化編程語(yǔ)言

2.2 語(yǔ)法上

C++具有 重載 、 繼承 、多態(tài)三種特性;

C++相比 C,增加多許多類(lèi)型安全的功能,比如強(qiáng)制類(lèi)型轉(zhuǎn)換;

C++支持 范式編程 ,比如模板類(lèi)、函數(shù)模板等。

3 C++中四種cast轉(zhuǎn)換

C++中四種類(lèi)型轉(zhuǎn)換是:static_cast, dynamic_cast, const_cast, reinterpret_cast

3.1 const_cast

用于將 const 變量轉(zhuǎn)為 非 const 。它也是四個(gè)強(qiáng)制類(lèi)型轉(zhuǎn)換運(yùn)算符中唯一能夠去除 const 屬性的運(yùn)算符。對(duì)于未定義 const 版本的成員函數(shù),我們通常需要使用 const_cast 來(lái)去除 const引用對(duì)象的 const,完成函數(shù)調(diào)用。另外一種使用方式,結(jié)合 static_cast,可以在非 const 版本的成員函數(shù)內(nèi)添加 const,調(diào)用完 const 版本的成員函數(shù)后,再使用 const_cast 去除 const限定。

3.2 static_cast

static_cast< new_type >(expression)
// new_type 為目標(biāo)數(shù)據(jù)類(lèi)型,expression 為原始數(shù)據(jù)類(lèi)型變量或者表達(dá)式。

基本數(shù)據(jù)類(lèi)型之間的轉(zhuǎn)換,如int、float、char之間的互相轉(zhuǎn)換;用于各種 隱式轉(zhuǎn)換 ,比如非 const 轉(zhuǎn) const,void*轉(zhuǎn)指針等,但 沒(méi)有運(yùn)行時(shí)類(lèi)型檢查來(lái)保證轉(zhuǎn)換的安全性 。

隱式類(lèi)型轉(zhuǎn)換 :首先,對(duì)于內(nèi)置類(lèi)型,低精度的變量給高精度變量賦值會(huì)發(fā)生隱式類(lèi)型轉(zhuǎn)換,其次,對(duì)于只存在單個(gè)參數(shù)的構(gòu)造函數(shù)的對(duì)象構(gòu)造來(lái)說(shuō),函數(shù)調(diào)用可以直接使用該參數(shù)傳入,編譯器會(huì)自動(dòng)調(diào)用其構(gòu)造函數(shù)生成 臨時(shí)對(duì)象 。

static_cast主要有如下幾種用法:

  1. 用于類(lèi)層次結(jié)構(gòu)中基類(lèi)派生類(lèi)之間指針或引用的轉(zhuǎn)換。

    進(jìn)行向上轉(zhuǎn)換是安全的;
    進(jìn)行向下轉(zhuǎn)換時(shí),由于沒(méi)有動(dòng)態(tài)類(lèi)型檢查,所以是不安全的。因?yàn)?基類(lèi)不包含派生類(lèi)的成員變量,無(wú)法對(duì)派生類(lèi)的成員變量賦值。

  2. 用于基本數(shù)據(jù)類(lèi)型之間的轉(zhuǎn)換,如int、float、char之間的互相轉(zhuǎn)換

  3. 把空指針轉(zhuǎn)換成 目標(biāo)類(lèi)型的空指針 。

  4. 把任何類(lèi)型的表達(dá)式轉(zhuǎn) 換成void類(lèi)型

注意:static_cast不能去掉expression的const、volatile、或者_(dá)_unaligned屬性。

char a = 'a'; int b = static_cast<char>(a);  //將char型數(shù)據(jù)轉(zhuǎn)換成int型數(shù)據(jù)

const int g = 20;
int *h = static_cast<int*>(&g);   //編譯錯(cuò)誤,static_cast不能去掉g的const屬性
class Base
{};
class Derived : public Base
{}

Base* pB = new Base();
if(Derived* pD = static_cast(pB))
{}  //下行轉(zhuǎn)換是不安全的(堅(jiān)決抵制這種方法)

Derived* pD = new Derived();
if(Base* pB = static_cast(pD))
{}   //上行轉(zhuǎn)換是安全的

3.3 dynamic_cast

dynamic_cast< new_type >(expression)
// new_type 為目標(biāo)數(shù)據(jù)類(lèi)型,expression 為原始數(shù)據(jù)類(lèi)型變量或者表達(dá)式。
dynamic_cast< type* >(e)   //type必須是一個(gè)類(lèi)類(lèi)型且必須是一個(gè)有效的指針 
dynamic_cast< type& >(e)   //type必須是一個(gè)類(lèi)類(lèi)型且必須是一個(gè)左值 
dynamic_cast< type&& >(e)  //type必須是一個(gè)類(lèi)類(lèi)型且必須是一個(gè)右值

用于 動(dòng)態(tài)類(lèi)型轉(zhuǎn)換 。只能用于 含有虛函數(shù)的類(lèi) ,用于類(lèi)層次間的向上向下轉(zhuǎn)化、類(lèi)之間的 交叉轉(zhuǎn)換 (cross cast)。只能轉(zhuǎn)指針引用 。

在類(lèi)層次間向上轉(zhuǎn)換時(shí),dynamic_cast和static_cast的效果是一樣的;在進(jìn)行向下轉(zhuǎn)換時(shí),dynamic_cast具有類(lèi)型檢查的功能,它通過(guò)判斷在執(zhí)行到該語(yǔ)句的時(shí)候,變量類(lèi)型和要轉(zhuǎn)換的類(lèi)型是否相同來(lái)判斷是否能夠進(jìn)行向下轉(zhuǎn)換,如果是非法的對(duì)于轉(zhuǎn)換目標(biāo)是指針類(lèi)型返回 NULL,對(duì)于引用拋std::bad_cast異常比static_cast更安全。

3.4 reinterpret_cast

幾乎什么都可以轉(zhuǎn),比如將 int 轉(zhuǎn)指針,執(zhí)行的是逐個(gè)比特復(fù)制的操作。容易出問(wèn)題,盡量少用。

3.5 為何不用C的強(qiáng)制轉(zhuǎn)換

C 的強(qiáng)制轉(zhuǎn)換表面上看起來(lái)功能強(qiáng)大什么都能轉(zhuǎn),但是轉(zhuǎn)化不夠明確,不能進(jìn)行錯(cuò)誤檢查,容易出錯(cuò)。

4 C/C++中指針和引用的區(qū)別

4.1 指針

指針利用地址,它的值直接指向存在電腦存儲(chǔ)器中另一個(gè)地方的值。由于通過(guò)地址能找到所需的變量單元,可以說(shuō),地址指向該變量單元。因此,將地址形象化的稱(chēng)為“指針”。意思是通過(guò)它能找到以它為地址的內(nèi)存單元。

4.2 引用

引用就是某一變量的一個(gè) 別名 ,對(duì)引用的操作與對(duì)變量直接操作完全一樣。引用的聲明方法:類(lèi)型標(biāo)識(shí)符 &引用名=目標(biāo)變量名;引用引入了對(duì)象的一個(gè)同義詞。定義引用的表示方法與定義指針相似,只是用&代替了*

4.3 區(qū)別

  1. 指針有自己的一塊空間,而引用只是一個(gè)別名;
  2. 使用 sizeof 看一個(gè)指針的大小是 4,而引用則是被引用對(duì)象的大小;
  3. 指針可以被初始化為 NULL,而引用必須被初始化且必須是一個(gè)已有對(duì)象的引用;
  4. 作為參數(shù)傳遞時(shí),指針需要被解引用才可以對(duì)對(duì)象進(jìn)行操作,而對(duì)引用的修改都會(huì)改變引用所指向的對(duì)象;
  5. 可以有 const 指針,但是沒(méi)有 const 引用;
  6. 指針在使用中可以指向其它對(duì)象,但是引用只能是一個(gè)對(duì)象的引用,不能 被改變;
  7. 指針可以有多級(jí)指針(**p),而引用至于一級(jí);
  8. 指針和引用使用++運(yùn)算符的意義不一樣;
  9. 如果返回動(dòng)態(tài)內(nèi)存分配的對(duì)象或者內(nèi)存,必須使用指針,引用可能引起內(nèi)存泄露。

5 C++智能指針

C++里面的四個(gè)智能指針: auto_ptr , shared_ptr , weak_ptr , unique_ptr 其中后三個(gè)是c++11 支持,并且第一個(gè)已經(jīng)被 11 棄用。

為什么要使用智能指針:

智能指針的作用是 管理一個(gè)指針 ,因?yàn)榇嬖谝韵逻@種情況:申請(qǐng)的空間在函數(shù)結(jié)束時(shí) 忘記釋放 ,造成 內(nèi)存泄漏 。使用智能指針可以很大程度上的避免這個(gè)問(wèn)題,因?yàn)橹悄苤羔樉褪且粋€(gè) 類(lèi) ,當(dāng)超出了類(lèi)的作用域是,類(lèi)會(huì) 自動(dòng)調(diào)用析構(gòu)函數(shù) ,析構(gòu)函數(shù)會(huì)自動(dòng)釋放資源。所以智能指針的作用原理就是在函數(shù)結(jié)束時(shí)自動(dòng)釋放內(nèi)存空間,不需要手動(dòng)釋放內(nèi)存空間。

對(duì) shared_ptr 進(jìn)行初始化時(shí)不能將一個(gè)普通指針直接賦值給智能指針,因?yàn)橐粋€(gè)是指針,一個(gè)是類(lèi)??梢酝ㄟ^(guò) make_shared 函數(shù)或者通過(guò)構(gòu)造函數(shù)傳入普通指針。并可以通過(guò) get 函數(shù)獲得普通指針。

5.1 auto_ptr

c++98 的方案,cpp11 已經(jīng)拋棄。

auto_ptr< string> p1 (new string ("I reigned lonely as a cloud.”));
auto_ptr p2;
p2 = p1; //auto_ptr 不會(huì)報(bào)錯(cuò).

此時(shí)不會(huì)報(bào)錯(cuò), p2 剝奪了 p1 的所有權(quán) ,但是當(dāng)程序運(yùn)行時(shí)訪問(wèn) p1 將會(huì)報(bào)錯(cuò)。所以 auto_ptr存在潛在的內(nèi)存***問(wèn)題。

5.2 unique_ptr

替換 auto_ptr。unique_ptr 實(shí)現(xiàn)獨(dú)占式擁有或嚴(yán)格擁有概念,保證同一時(shí)間內(nèi)只有一個(gè)智能指針可以指向該對(duì)象。它對(duì)于避免資源泄露(例如:以 new 創(chuàng)建對(duì)象后因?yàn)榘l(fā)生異常而忘記調(diào)用 delete)特別有用。還是上面那個(gè)例子:

unique_ptr<string> p3 (new string ("auto")); //#4
unique_ptr<string> p4; //#5
p4 = p3;  //此時(shí)會(huì)報(bào)錯(cuò)!!

編譯器認(rèn)為 p4=p3 非法,避免了 p3 不再指向有效數(shù)據(jù)的問(wèn)題。因此,unique_ptr 比 auto_ptr更安全。另外unique_ptr 還有更聰明的地方:當(dāng)程序試圖將一個(gè) unique_ptr 賦值給另一個(gè)時(shí),如果源 unique_ptr 是個(gè)臨時(shí)右值,編譯器允許這么做;如果源 unique_ptr 將存在一段時(shí)間,編譯器將禁止這么做,比如:

unique_ptr<string> pu1(new string ("hello world"));
unique_ptr<string> pu2;
pu2 = pu1; // #1 not allowed
unique_ptr<string> pu3;
pu3 = unique_ptr<string>(new string ("You")); // #2 allowed

其中#1 留下懸掛的 unique_ptr(pu1),這可能導(dǎo)致危害。而#2 不會(huì)留下懸掛的 unique_ptr,因?yàn)樗{(diào)用unique_ptr 的構(gòu)造函數(shù),該構(gòu)造函數(shù)創(chuàng)建的臨時(shí)對(duì)象在其所有權(quán)讓給 pu3 后就會(huì)被銷(xiāo)毀。這種隨情況而已的行為表明,unique_ptr 優(yōu)于允許兩種賦值的 auto_ptr 。

注:如果確實(shí)想執(zhí)行類(lèi)似與#1 的操作,要安全的重用這種指針,可給它賦新值。C++有一個(gè)標(biāo)準(zhǔn)庫(kù)函數(shù) std::move(),讓你能夠?qū)⒁粋€(gè) unique_ptr 賦給另一個(gè)。例如:

unique_ptr<string> ps1, ps2;
ps1 = demo("hello");
ps2 = move(ps1);
ps1 = demo("alexia");
cout << *ps2 << *ps1 << endl;

5.3 shared_ptr

shared_ptr 實(shí)現(xiàn)共享式擁有概念。多個(gè)智能指針可以指向相同對(duì)象,該對(duì)象和其相關(guān)資源會(huì)在“最后一個(gè)引用被銷(xiāo)毀”時(shí)候釋放。從名字 share 就可以看出了資源可以被多個(gè)指針共享,它使用計(jì)數(shù)機(jī)制來(lái)表明資源被幾個(gè)指針共享。可以通過(guò)成員函數(shù) use_count()來(lái)查看資源的所有者個(gè)數(shù)。除了可以通過(guò) new 來(lái)構(gòu)造,還可以通過(guò)傳入auto_ptr, unique_ptr,weak_ptr 來(lái)構(gòu)造。當(dāng)我們調(diào)用 release()時(shí),當(dāng)前指針會(huì)釋放資源所有權(quán),計(jì)數(shù)減一。當(dāng)計(jì)數(shù)等于 0 時(shí),資源會(huì)被釋放。

shared_ptr 是為了解決 auto_ptr 在對(duì)象所有權(quán)上的局限性(auto_ptr 是獨(dú)占的), 在使用引用計(jì)數(shù)的機(jī)制上提供了可以共享所有權(quán)的智能指針。

成員函數(shù):

  • use_count 返回引用計(jì)數(shù)的個(gè)數(shù)
  • unique 返回是否是獨(dú)占所有權(quán)( use_count 為 1)
  • swap 交換兩個(gè) shared_ptr 對(duì)象(即交換所擁有的對(duì)象)
  • reset 放棄內(nèi)部對(duì)象的所有權(quán)或擁有對(duì)象的變更, 會(huì)引起原有對(duì)象的引用計(jì)數(shù)的減少
  • get 返回內(nèi)部對(duì)象(指針), 由于已經(jīng)重載了()方法, 因此和直接使用對(duì)象是一樣的。如:
    shared_ptr<int> sp(new int(1)); // sp 與 sp.get()是等價(jià)的
    

5.4 weak_ptr

weak_ptr 是一種不控制對(duì)象生命周期的智能指針, 它指向一個(gè) shared_ptr 管理的對(duì)象。weak_ptr 設(shè)計(jì)的目的是為協(xié)助shared_ptr 而引入的一種智能指針,它只可以從一個(gè) shared_ptr 或另一個(gè) weak_ptr 對(duì)象構(gòu)造,它的構(gòu)造和析構(gòu)不會(huì)引起引用記數(shù)的增加或減少。

weak_ptr 是用來(lái) 解決 shared_ptr 相互引用時(shí)的死鎖問(wèn)題 ,如果說(shuō)兩個(gè) shared_ptr 相互引用,那么這兩個(gè)指針的引用計(jì)數(shù)永遠(yuǎn)不可能下降為 0,資源永遠(yuǎn)不會(huì)釋放。它是對(duì)對(duì)象的一種弱引用,不會(huì)增加對(duì)象的引用計(jì)數(shù),和 shared_ptr 之間可以相互轉(zhuǎn)化,shared_ptr 可以直接賦值給它,它可以通過(guò)調(diào)用 lock 函數(shù)來(lái)獲得 shared_ptr。

class B;
class A
{
	public:
	shared_ptr pb_;
	~A()
	{
		cout<<"A delete\\n";
	}
};
class B
{
	public:
	shared_ptr pa_;
	~B()
	{
		cout<<"B delete\\n";
	}
};
void fun()
{
	shared_ptr pb(new B());
	shared_ptr pa(new A());
	pb->pa_ = pa;
	pa->pb_ = pb;
	cout<endl;
	cout<endl;
}
int main()
{
	fun();
	return 0;
}

可以看到 fun 函數(shù)中 pa,pb 之間互相引用,兩個(gè)資源的引用計(jì)數(shù)為 2,當(dāng)要跳出函數(shù)時(shí),智能指針 pa,pb 析構(gòu)時(shí)兩個(gè)資源引用計(jì)數(shù)會(huì)減一,但是兩者引用計(jì)數(shù)還是為 1,導(dǎo)致跳出函數(shù)時(shí)資源沒(méi)有被釋放(A B 的析構(gòu)函數(shù)沒(méi)有被調(diào)用),如果把其中一個(gè)改為 weak_ptr 就可以了,我們把類(lèi) A 里面的 shared_ptr pb改為 weak_ptr pb運(yùn)行結(jié)果如下,這樣的話,資源 B 的引用開(kāi)始就只有 1,當(dāng) pb 析構(gòu)時(shí),B 的計(jì)數(shù)變?yōu)?0,B 得到釋放,B 釋放的同時(shí)也會(huì)使 A 的計(jì)數(shù)減一,同時(shí) pa 析構(gòu)時(shí)使 A 的計(jì)數(shù)減一,那么 A 的計(jì)數(shù)為 0,A 得到釋放。

注意的是我們不能通過(guò) weak_ptr 直接訪問(wèn)對(duì)象的方法,比如 B 對(duì)象中有一個(gè)方法 print(),我們不能這樣訪問(wèn),pa->pb->print(); 英文 pb_是一個(gè) weak_ptr,應(yīng)該先把它轉(zhuǎn)化為shared_ptr,如:

shared_ptr p = pa->pb_.lock(); 
p->print();

5.5 內(nèi)存泄露

當(dāng)兩個(gè)對(duì)象相互使用一個(gè) shared_ptr 成員變量指向?qū)Ψ?,?huì)造成 循環(huán)引用 ,使引用計(jì)數(shù)失效,從而導(dǎo)致內(nèi)存泄漏。

#include   
#include   
using namespace std;  
  
class B;  
class A  
{  
public:  // 為了省去一些步驟這里 數(shù)據(jù)成員也聲明為public  
    shared_ptr pb;  
    ~A()  
    {  
        cout << "kill A\\n";  
    }  
};  
class B  
{  
public:  
    shared_ptr pa;  
    ~B()  
    {  
        cout <<"kill B\\n";  
    }  
};  
int main(int argc, char** argv)  
{  
    shared_ptr sa(new A());  
    shared_ptr sb(new B());  
    if(sa && sb)  
    {  
        sa->pb=sb;  
        sb->pa=sa;  
    }  
    cout<<"sa use count:"<use_count()<return 0;  
}

注意此時(shí)sa,sb都沒(méi)有釋放,產(chǎn)生了內(nèi)存泄露問(wèn)題。即A內(nèi)部有指向B,B內(nèi)部有指向A,這樣對(duì)于A,B必定是在A析構(gòu)后B才析構(gòu),對(duì)于B,A必定是在B析構(gòu)后才析構(gòu)A,這就是循環(huán)引用問(wèn)題,違反常規(guī),導(dǎo)致內(nèi)存泄露。

解決辦法

使用弱引用的智能指針weak_ptr打破這種循環(huán)引用。為了解決循環(huán)引用導(dǎo)致的內(nèi)存泄漏,引入了weak_ptr 弱指針,weak_ptr 的構(gòu)造函數(shù)不會(huì)修改引用計(jì)數(shù)的值,從而不會(huì)對(duì)對(duì)象的內(nèi)存進(jìn)行管理,其類(lèi)似一個(gè)普通指針,但不指向引用計(jì)數(shù)的共享內(nèi)存,但是其可以檢測(cè)到所管理的對(duì)象是否已經(jīng)被釋放,從而避免非法訪問(wèn)。

5.6 shared_ptr的實(shí)現(xiàn)

template <typename T>
class SmartPtr
{
private:
    T *ptr; //底層真實(shí)的指針
    int *use_count;//保存當(dāng)前對(duì)象被多少指針引用計(jì)數(shù)
public:
    SmartPtr(T *p); //SmartPtrp(new int(2));
    SmartPtr(const SmartPtr&orig);//SmartPtrq(p);
    SmartPtr&operator=(const SmartPtr &rhs);//q=p
    ~SmartPtr();
    T operator*(); //為了能把智能指針當(dāng)成普通指針操作定義解引用操作
    T*operator->(); //定義取成員操作
    T* operator+(int i);//定義指針加一個(gè)常數(shù)
    int operator-(SmartPtr&t1, SmartPtr&t2);//定義兩個(gè)指針相減
    void getcount()
    {
        return *use_count
    }
};

template <typename T>
int SmartPtr::operator-(SmartPtr &t1, SmartPtr &t2)
{
    return t1.ptr - t2.ptr;
}
template <typename T>
SmartPtr::SmartPtr(T *p)
{
    ptr = p;
    try
    {
        use_count = new int(1);
    }
    catch (...)
    {
        delete ptr; //申請(qǐng)失敗釋放真實(shí)指針和引用計(jì)數(shù)的內(nèi)存
        ptr = nullptr;
        delete use_count;
        use_count = nullptr;
    }
}
template <typename T>
SmartPtr::SmartPtr(const SmartPtr &orig) //復(fù)制構(gòu)造函數(shù)
{
    use_count = orig.use_count;//引用計(jì)數(shù)保存在一塊內(nèi)存,所有的 SmarPtr 對(duì)象的引用計(jì)數(shù)
    都指向這里
        this->ptr = orig.ptr;
    ++(*use_count); //當(dāng)前對(duì)象的引用計(jì)數(shù)加 1
}
template <typename T>
SmartPtr& SmartPtr::operator=(const SmartPtr &rhs)
{
    //重載=運(yùn)算符,例如 SmartPtrp,q; p=q;這個(gè)語(yǔ)句中,首先給 q 指向的對(duì)象的引用計(jì)數(shù)加1,因?yàn)?p 重新指向了 q 所指的對(duì)象,所以 p 需要先給原來(lái)的對(duì)象的引用計(jì)數(shù)減 1,如果減一后為 0,先釋放掉 p 原來(lái)指向的內(nèi)存,然后講 q 指向的對(duì)象的引用計(jì)數(shù)加 1 后賦值給 p
    ++*(rhs.use_count);
    if ((--*(use_count)) == 0)
    {
        delete ptr;
        ptr = nullptr;
        delete use_count;
        use_count = nullptr;
    }
    ptr = rhs.ptr;
    *use_count = *(rhs.use_count);
    return *this;
}
template <typename T>
SmartPtr::~SmartPtr()
{
    getcount();
    if (--(*use_count) == 0) //SmartPtr 的對(duì)象會(huì)在其生命周期結(jié)束的時(shí)候調(diào)用其析構(gòu)函數(shù),在析構(gòu)函數(shù)中檢測(cè)當(dāng)前對(duì)象的引用計(jì)數(shù)是不是只有正在結(jié)束生命周期的這個(gè) SmartPtr 引用,如果是,就釋放掉,如果不是,就還有其他的 SmartPtr 引用當(dāng)前對(duì)象,就等待其他的 SmartPtr對(duì)象在其生命周期結(jié)束的時(shí)候調(diào)用析構(gòu)函數(shù)釋放掉
    {
        getcount();
        delete ptr;
        ptr = nullptr;
        delete use_count;
        use_count = nullptr;
    }
}
template <typename T>
T SmartPtr::operator*()
{
    return *ptr;
}
template <typename T>
T* SmartPtr::operator->()
{
    return ptr;
}
template <typename T>
T* SmartPtr::operator+(int i)
{
    T *temp = ptr + i;
    return temp;
}

6 數(shù)組和指針

指針 數(shù)組
保存數(shù)據(jù)的地址 保存數(shù)據(jù)
指針的內(nèi)容為為地址,從該地址訪問(wèn)數(shù)據(jù) 直接訪問(wèn)數(shù)據(jù)
通常用于動(dòng)態(tài)的數(shù)據(jù)結(jié)構(gòu) 通常用于固定數(shù)目且數(shù)據(jù)類(lèi)型相同的元素
通過(guò) Malloc 分配內(nèi)存,free 釋放內(nèi)存 隱式的分配和刪除
通常指向匿名數(shù)據(jù),操作匿名函數(shù) 自身即為數(shù)據(jù)名

7 野指針

野指針就是指向一個(gè)已刪除的對(duì)象或者未申請(qǐng)?jiān)L問(wèn)受限內(nèi)存區(qū)域的指針

8 函數(shù)指針

8.1 定義

函數(shù)指針是指向函數(shù)的指針變量。

函數(shù)指針本身首先是一個(gè)指針變量,該指針變量指向一個(gè)具體的函數(shù)。這正如用指針變量可指向整型變量、字符型、數(shù)組一樣,這里是指向函數(shù)。

C 在編譯時(shí),每一個(gè)函數(shù)都有一個(gè)入口地址,該入口地址就是函數(shù)指針?biāo)赶虻牡刂?。有了指向函?shù)的指針變量后,可用該指針變量調(diào)用函數(shù),就如同用指針變量可引用其他類(lèi)型變量一樣,在這些概念上是大體一致的。

8.2 用途:

調(diào)用函數(shù)和做函數(shù)的參數(shù),比如回調(diào)函數(shù)。

8.3 示例:

char * fun(char * p) {…}   // 函數(shù) fun
char * (*pf)(char * p);    // 函數(shù)指針 pf
pf = fun;                  // 函數(shù)指針 pf 指向函數(shù) fun
pf(p);                     // 通過(guò)函數(shù)指針 pf 調(diào)用函數(shù) fun

9 fork函數(shù)

Fork:創(chuàng)建一個(gè)和當(dāng)前進(jìn)程映像一樣的進(jìn)程可以通過(guò) fork( )系統(tǒng)調(diào)用:

#include 
#include 
pid_t fork(void);

成功調(diào)用 fork( )會(huì) 創(chuàng)建一個(gè)新的進(jìn)程 ,它幾乎與調(diào)用 fork( )的進(jìn)程一模一樣,這兩個(gè)進(jìn)程都會(huì)繼續(xù)運(yùn)行。在子進(jìn)程中,成功的 fork( )調(diào)用會(huì)返回 0。在父進(jìn)程中 fork( )返回子進(jìn)程的 pid。如果出現(xiàn)錯(cuò)誤,fork( )返回一個(gè)負(fù)值。

最常見(jiàn)的 fork( )用法是創(chuàng)建一個(gè)新的進(jìn)程,然后使用 **exec( )**載入二進(jìn)制映像,替換當(dāng)前進(jìn)程的映像。這種情況下,派生(fork)了新的進(jìn)程,而這個(gè)子進(jìn)程會(huì)執(zhí)行一個(gè)新的二進(jìn)制可執(zhí)行文件的映像。這種“派生加執(zhí)行”的方式是很常見(jiàn)的。

在早期的 Unix 系統(tǒng)中,創(chuàng)建進(jìn)程比較原始。當(dāng)調(diào)用 fork 時(shí),內(nèi)核會(huì)把所有的內(nèi)部數(shù)據(jù)結(jié)構(gòu)復(fù)制一份,復(fù)制進(jìn)程的頁(yè)表項(xiàng),然后把父進(jìn)程的地址空間中的內(nèi)容逐頁(yè)的復(fù)制到子進(jìn)程的地址空間中。但從內(nèi)核角度來(lái)說(shuō),逐頁(yè)的復(fù)制方式是十分耗時(shí)的?,F(xiàn)代的 Unix 系統(tǒng)采取了更多的優(yōu)化,例如 Linux,采用了寫(xiě)時(shí)復(fù)制的方法,而不是對(duì)父進(jìn)程空間進(jìn)程整體復(fù)制。

10 析構(gòu)函數(shù)

析構(gòu)函數(shù)與構(gòu)造函數(shù)對(duì)應(yīng),當(dāng)對(duì)象結(jié)束其生命周期,如對(duì)象所在的函數(shù)已調(diào)用完畢時(shí),系統(tǒng)會(huì)自動(dòng)執(zhí)行析構(gòu)函數(shù)。

析構(gòu)函數(shù)名也應(yīng)與類(lèi)名相同,只是在函數(shù)名前面加一個(gè)位取反符~,例如~stud( ),以區(qū)別于構(gòu)造函數(shù)。它 不能帶任何參數(shù),也沒(méi)有返回值 (包括 void 類(lèi)型)。只能有一個(gè)析構(gòu)函數(shù), 不能重載 。

如果用戶(hù)沒(méi)有編寫(xiě)析構(gòu)函數(shù),編譯系統(tǒng)會(huì)自動(dòng)生成一個(gè)缺省的析構(gòu)函數(shù)(即使自定義了析構(gòu)函數(shù),編譯器也總是會(huì)為我們合成一個(gè)析構(gòu)函數(shù),并且如果自定義了析構(gòu)函數(shù),編譯器在執(zhí)行時(shí)會(huì)先調(diào)用自定義的析構(gòu)函數(shù)再調(diào)用合成的析構(gòu)函數(shù)),它也不進(jìn)行任何操作。所以許多簡(jiǎn)單的類(lèi)中沒(méi)有用顯式的析構(gòu)函數(shù)。

如果一個(gè)類(lèi)中有指針,且在使用的過(guò)程中動(dòng)態(tài)的申請(qǐng)了內(nèi)存,那么最好顯示構(gòu)造析構(gòu)函數(shù)在銷(xiāo)毀類(lèi)之前,釋放掉申請(qǐng)的內(nèi)存空間,避免內(nèi)存泄漏。

10.1 類(lèi)析構(gòu)順序

  1. 派生類(lèi)本身的析構(gòu)函數(shù)
  2. 對(duì)象成員析構(gòu)函數(shù)
  3. 基類(lèi)析構(gòu)函數(shù)

因?yàn)槲鰳?gòu)函數(shù)沒(méi)有參數(shù),所以包含成員對(duì)象的類(lèi)的析構(gòu)函數(shù)形式上并無(wú)特殊之處。但在撤銷(xiāo)該類(lèi)對(duì)象的時(shí)候,會(huì)首先調(diào)用自己的析構(gòu)函數(shù),再調(diào)用成員對(duì)象的析構(gòu)函數(shù),調(diào)用次序與初始化時(shí)的次序相反。

11 虛函數(shù)和多態(tài)

多態(tài)的實(shí)現(xiàn)主要分為靜態(tài)多態(tài)動(dòng)態(tài)多態(tài) , 靜態(tài)多態(tài)主要是重載 ,在 編譯的時(shí)候就已經(jīng)確定動(dòng)態(tài)多態(tài)是用虛函數(shù)機(jī)制實(shí)現(xiàn)的,在 運(yùn)行期間動(dòng)態(tài)綁定 。例如:一個(gè)父類(lèi)類(lèi)型的指針指向一個(gè)子類(lèi)對(duì)象時(shí)候,使用父類(lèi)的指針去調(diào)用子類(lèi)中重寫(xiě)了的父類(lèi)中的虛函數(shù)的時(shí)候,會(huì)調(diào)用子類(lèi)重寫(xiě)過(guò)后的函數(shù),在父類(lèi)中聲明為加了 virtual 關(guān)鍵字的函數(shù),在子類(lèi)中重寫(xiě)時(shí)候不需要加 virtual也是虛函數(shù)。

虛函數(shù)的實(shí)現(xiàn):在有虛函數(shù)的類(lèi)中,類(lèi)的最開(kāi)始部分是一個(gè)虛函數(shù)表的 指針 ,這個(gè)指針指向一個(gè) 虛函數(shù)表 ,表中放了虛函數(shù)的地址,實(shí)際的虛函數(shù)在代碼段(.text)中。當(dāng)子類(lèi)繼承了父類(lèi)的時(shí)候也會(huì)繼承其虛函數(shù)表,當(dāng)子類(lèi)重寫(xiě)父類(lèi)中虛函數(shù)時(shí)候,會(huì)將其繼承到的 虛函數(shù)表中的地址替換為重新寫(xiě)的函數(shù)地址 。使用了虛函數(shù),會(huì)增加訪問(wèn)內(nèi)存開(kāi)銷(xiāo),降低效率。

12 析構(gòu)函數(shù)與虛函數(shù)

析構(gòu)函數(shù)必須是虛函數(shù),因?yàn)閷⒖赡軙?huì)被繼承的父類(lèi)的析構(gòu)函數(shù)設(shè)置為虛函數(shù),可以保證當(dāng)我們 new 一個(gè)子類(lèi),然后使用基類(lèi)指針指向該子類(lèi)對(duì)象, 釋放基類(lèi)指針時(shí)可以釋放掉子類(lèi)的空間 ,防止內(nèi)存泄漏。

C++默認(rèn)的析構(gòu)函數(shù)不是虛函數(shù)是因?yàn)樘摵瘮?shù)需要額外的虛函數(shù)表和虛表指針,占用額外的內(nèi)存。而對(duì)于不會(huì)被繼承的類(lèi)來(lái)說(shuō),其析構(gòu)函數(shù)如果是虛函數(shù),就會(huì)浪費(fèi)內(nèi)存。因此 C++默認(rèn)的析構(gòu)函數(shù)不是虛函數(shù),而是只有當(dāng)需要當(dāng)作父類(lèi)時(shí), 設(shè)置虛函數(shù) 。

13 靜態(tài)函數(shù)和虛函數(shù)

靜態(tài)函數(shù)在編譯的時(shí)候就已經(jīng)確定運(yùn)行時(shí)機(jī),虛函數(shù)在運(yùn)行的時(shí)候動(dòng)態(tài)綁定。虛函數(shù)因?yàn)橛昧颂摵瘮?shù)表機(jī)制,調(diào)用的時(shí)候會(huì)增加一次內(nèi)存開(kāi)銷(xiāo)。

13.1 靜態(tài)函數(shù)

用static修飾的函數(shù),限定在本源碼文件中使用,不能被本源碼文件以外的代碼文件調(diào)用。 普通的函數(shù),默認(rèn)是extern的,也就是說(shuō),可以被其它代碼文件調(diào)用該函數(shù)。

13.2 虛函數(shù)表

當(dāng)一個(gè)類(lèi)中包含被virtual 關(guān)鍵字修飾的成員函數(shù)時(shí),該成員函數(shù)就成為了一個(gè) 虛函數(shù) 。頭一個(gè)含有虛函數(shù)的類(lèi)所實(shí)例化出來(lái)的對(duì)象都擁有同一個(gè) 虛函數(shù)表 ,在對(duì)象中含有一個(gè)* 虛函數(shù)指針 _vptr ,該指針指向該類(lèi)的虛函數(shù)表,虛函數(shù)表保存的是類(lèi)中虛函數(shù)的地址(一個(gè)類(lèi)可能有多個(gè)虛函數(shù))。

13.3 虛函數(shù)作用

當(dāng)一個(gè)子類(lèi)繼承了一個(gè)含有虛函數(shù)的基類(lèi),并重寫(xiě)了該基類(lèi)中的一個(gè)虛函數(shù),我們就說(shuō)這兩個(gè)類(lèi)構(gòu)成多態(tài)。子類(lèi)繼承基類(lèi)的同時(shí),基類(lèi)的虛函數(shù)表也被子類(lèi)繼承,不同的是被 子類(lèi)重寫(xiě)的虛函數(shù)將會(huì)替代原來(lái)虛函數(shù)表中對(duì)應(yīng)的基類(lèi)的虛函數(shù)的地址 。從而基類(lèi)與子類(lèi)調(diào)用同名的虛函數(shù)時(shí),所調(diào)用的就不是同一個(gè)函數(shù),從而體現(xiàn)了多態(tài)和虛函數(shù)表的作用。

13.4 靜態(tài)函數(shù)與虛函數(shù)的區(qū)別

我們知道類(lèi)的靜態(tài)函數(shù)是沒(méi)有this指針的,調(diào)用它時(shí)不需要?jiǎng)?chuàng)建對(duì)象,通過(guò):類(lèi)名 ::函數(shù)名(參數(shù))的形式直接調(diào)用。靜態(tài)函數(shù)只有唯一的一份,因此它的 地址是固定不變的 , 所以編譯的時(shí)候但凡遇到調(diào)用該靜態(tài)函數(shù)的時(shí)候就知道調(diào)用的是哪一個(gè)函數(shù),因此說(shuō)靜態(tài)函數(shù)在編譯的時(shí)候就已經(jīng)確定運(yùn)行時(shí)機(jī)。 而虛函數(shù)則不然,看下面的代碼:

class A
{
	public: 
	virtual void fun()
    {
        cout<<"i am A <I  am  B" <fun();
	return 0; 
}

類(lèi)A與類(lèi)B構(gòu)成多態(tài),創(chuàng)建了 A類(lèi)指針pb指向 B類(lèi)對(duì)象,當(dāng)程序編譯的時(shí)候只對(duì)語(yǔ)法等進(jìn)行檢測(cè),該語(yǔ)句沒(méi)有什么問(wèn)題,但是編譯器此時(shí)無(wú)法確定調(diào)用的是哪一個(gè) fun() 函數(shù),因?yàn)轭?lèi)A類(lèi)B中都含有fun函數(shù),因此只能是在程序運(yùn)行的時(shí)候通過(guò) pb指針查看對(duì)象的虛函數(shù)表(訪問(wèn)虛函數(shù)表就是所謂的訪問(wèn)內(nèi)存)才能確定該函數(shù)的地址,即確定調(diào)用的是哪一個(gè)函數(shù)。這就解釋了所說(shuō)的“ 虛函數(shù)在運(yùn)行的時(shí)候動(dòng)態(tài)綁定。虛函數(shù)因?yàn)橛昧颂摵瘮?shù)表機(jī)制,調(diào)用的時(shí)候會(huì)增加一次內(nèi)存開(kāi)銷(xiāo)。

14 重載和覆蓋

14.1 重載

兩個(gè)函數(shù)名相同,但是參數(shù)列表不同(個(gè)數(shù),類(lèi)型),返回值類(lèi)型沒(méi)有要求,在同一作用域中。

14.2 重寫(xiě)

子類(lèi)繼承了父類(lèi),父類(lèi)中的函數(shù)是虛函數(shù),在子類(lèi)中重新定義了這個(gè)虛函數(shù),這種情況是重寫(xiě),是一種同名覆蓋。

15 在main函數(shù)前先運(yùn)行的函數(shù)

1.test0__attribute((constructor))是gcc擴(kuò)展,標(biāo)記這個(gè)函數(shù)應(yīng)當(dāng)在main函數(shù)之前執(zhí)行。同樣有一個(gè)__attribute((destructor)),標(biāo)記函數(shù)應(yīng)當(dāng)在程序結(jié)束之前(main結(jié)束之后,或者調(diào)用了exit后)執(zhí)行。

2.test1 :全局static變量的初始化在程序初始階段,先于main函數(shù)的執(zhí)行。

#include  
using namespace std;

__attribute((constructor)) void test0()
{
	printf("before main 0\\n");
}

int test1()
{
    cout << "before main 1" << endl;
    return 54;
}

static int i = test1();
int main(int argc, char** argv) 
{
    cout << "main function." <<endl;
    return 0;
}

在leetcode里經(jīng)常見(jiàn)到static,在main之前關(guān)閉cin與stdin的同步來(lái)“加快”速度的黑科技。

static int _ = []{
    cin.sync_with_stdio(false);
    return 0;
}();

16 內(nèi)存管理

在 C++ 中,虛擬內(nèi)存分為代碼段、數(shù)據(jù)段、BSS 段、堆區(qū)、文件映射區(qū)以及棧區(qū)六部分。

  1. 棧(stack) :程序 自動(dòng)分配 ,使用??臻g存儲(chǔ)函數(shù)的返回地址、參數(shù)、局部變量、返回值。
  2. 堆(heap)
  • :調(diào)用malloc 在堆區(qū)動(dòng)態(tài)分配內(nèi)存,調(diào)用 free 來(lái)手動(dòng)釋放。堆是操作系統(tǒng)所維護(hù)的一塊特殊內(nèi)存,它提供了動(dòng)態(tài)分配的功能。
  • 自由存儲(chǔ)區(qū) :由new 分配內(nèi)存,用來(lái) delete 手動(dòng)釋放。和堆類(lèi)似,通過(guò)new來(lái)申請(qǐng)的內(nèi)存區(qū)域可稱(chēng)為自由存儲(chǔ)區(qū)。
  1. 靜態(tài)/全局區(qū) :在 C++ 里面沒(méi)有區(qū)分bss和data。
  • bss段 :存儲(chǔ)未初始化的全局變量和靜態(tài)變量(局部+全局),以及所有被初始化為0的全局變量和靜態(tài)變量,Block Started by Symbol。
  • data段 :存儲(chǔ)程序中已初始化的全局變量和靜態(tài)變量。
  1. 代碼區(qū) (code segment 或 text segment):
  • 代碼段 :存放函數(shù)體的二進(jìn)制代碼,text段。
  • 常量區(qū) :只讀數(shù)據(jù),比如字符串常量,程序結(jié)束時(shí)由系統(tǒng)釋放。 rodata段 ,read only。

init段:程序初始化入口代碼,在main() 之前運(yùn)行。

17 常量const

常量是固定值,在程序執(zhí)行期間不會(huì)改變。常量可以是任何的基本數(shù)據(jù)類(lèi)型,可分為int、float、char、string和bool。常量定義必須初始化。

17.1 存儲(chǔ)區(qū)域

  1. 局部常量 ,存放在 棧區(qū) ;
  2. static/全局常量 ,存放在 靜態(tài)/全局存儲(chǔ)區(qū) ;
  3. 字面值常量 ,其值一望而知,存放在 常量區(qū)

17.2 const修飾成員函數(shù)

const 修飾的成員函數(shù)表明函數(shù)調(diào)用 不會(huì)對(duì)對(duì)象做出任何更改 ,事實(shí)上,如果確認(rèn)不會(huì)對(duì)對(duì)象做更改,就應(yīng)該為函數(shù)加上 const 限定,這樣無(wú)論 const 對(duì)象還是普通對(duì)象都可以調(diào)用該函數(shù)

若同時(shí)定義了兩個(gè)函數(shù),一個(gè)帶 const,一個(gè)不帶,這相當(dāng)于函數(shù)的 重載 。

18 代碼解析

18.1 strcpy和strlen

strcpy 是字符串拷貝函數(shù),原型:

char *strcpy(char* dest, const char *src);

從 src 逐字節(jié)拷貝到 dest,直到遇到'\\0'結(jié)束,因?yàn)闆](méi)有指定長(zhǎng)度,可能會(huì)導(dǎo)致拷貝越界,造成緩沖區(qū)溢出漏洞,安全版本是 strncpy 函數(shù)。

strlen 函數(shù)是計(jì)算字符串長(zhǎng)度的函數(shù),返回從開(kāi)始到'\\0'之間的字符個(gè)數(shù)。

18.2 ++i和i++

++i 實(shí)現(xiàn):

int& int::operator++()
{
    *this +=1return *this;
}

i++ 實(shí)現(xiàn):

const int int::operatorint)
{
    int oldValue = *this;
    ++(*this);
    return oldValue;
}

18.3 代碼的區(qū)別

(1)字符串 123 保存在 常量區(qū) ,const 本來(lái)是修飾 arr 指向的值,不能通過(guò) arr 去修改,但是字符串“123”在常量區(qū),本來(lái)就不能改變,所以加不加 const 效果都一樣:

const char * arr = "123";

(2)字符串 123 保存在常量區(qū),這個(gè) 和arr 指針指向的是同一個(gè)位置,同樣不能通過(guò) brr 去修改"123"的值:

char * brr = "123";

(3)這里 123 本來(lái)是在棧上的,但是編譯器可能會(huì)做某些優(yōu)化,將其放到常量區(qū):

const char crr[] = "123";

(4)字符串 123 保存在 棧區(qū) ,可以通過(guò) drr 去修改:

char drr[] = "123";

19 編程題

19.1 點(diǎn)是在三角形內(nèi)

給定三角形ABC和一點(diǎn)P(x,y,z),判斷點(diǎn)P是否在ABC內(nèi)。

根據(jù)面積法,如果P在三角形ABC內(nèi),那么三角形ABP的面積+三角形BCP的面積+三角形ACP的面積應(yīng)該等于三角形ABC的面積。

S=(x_1y_2+x_2y_3+x_3y_1-x_1y_3-x_2y_1-x_3y_2)/2

代碼如下:

#include 
#include 
using namespace std;
#define ABS_FLOAT_0 0.0001
struct point_float
{
    float x;
    float y;
};

float GetTriangleSquar(const point_float pt0, const point_float pt1, const point_float pt2)  // 計(jì)算三角形面積
{
    point_float AB, BC;
    AB.x = pt1.x - pt0.x;
    AB.y = pt1.y - pt0.y;
    BC.x = pt2.x - pt1.x;
    BC.y = pt2.y - pt1.y;
    return fabs((AB.x * BC.y - AB.y * BC.x)) / 2.0f;
}

bool IsInTriangle(const point_float A, const point_float B, const point_float C, const point_float D)  // 判斷給定一點(diǎn)是否在三角形內(nèi)或邊上
{
    float SABC, SADB, SBDC, SADC;
    SABC = GetTriangleSquar(A, B, C);
    SADB = GetTriangleSquar(A, D, B);
    SBDC = GetTriangleSquar(B, D, C);
    SADC = GetTriangleSquar(A, D, C);
    float SumSuqar = SADB + SBDC + SADC;
    if ((-ABS_FLOAT_0 < (SABC - SumSuqar)) && ((SABC - SumSuqar) < ABS_FLOAT_0))
    	return true;
    else
    	return false;
}

19.2 判斷一個(gè)數(shù)是二的倍數(shù)

判斷一個(gè)數(shù)是不是二的倍數(shù),即判斷該數(shù)二進(jìn)制末位是不是 0:

a % 2 == 0  
a & 0x0001 == 0  // 兩種辦法都可

19.3 一個(gè)數(shù)中有幾個(gè)1

可以直接逐位除十取余判斷:

int fun(long x)
{
    int _count = 0;
    while(x)
    {
        if(x % 10 == 1)
            ++_count;
            x /= 10;
    }
    return _count;
}
int main()
{
    cout << fun(123321) << endl;
    return 0;
}

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • C++
    C++
    +關(guān)注

    關(guān)注

    22

    文章

    2119

    瀏覽量

    75239
  • static
    +關(guān)注

    關(guān)注

    0

    文章

    34

    瀏覽量

    10711
  • 關(guān)鍵字
    +關(guān)注

    關(guān)注

    0

    文章

    37

    瀏覽量

    7060
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    【微信精選】C++的精髓類(lèi)和繼承就該這么理解!

    C++基礎(chǔ)知識(shí)說(shuō)明,如果你學(xué)過(guò)C語(yǔ)言,那么基礎(chǔ)知識(shí)是一樣的,比循環(huán)、運(yùn)算符、指針等…。可以說(shuō)C++是在C
    發(fā)表于 08-15 10:00

    【微信精選】C++的精髓類(lèi)和繼承就該這么理解!

    C++基礎(chǔ)知識(shí)說(shuō)明,如果你學(xué)過(guò)C語(yǔ)言,那么基礎(chǔ)知識(shí)是一樣的,比循環(huán)、運(yùn)算符、指針等…??梢哉f(shuō)C++是在C
    發(fā)表于 08-15 09:41

    分享一個(gè)開(kāi)源的ESP32 物聯(lián)網(wǎng)小工具

    固件所需的 C++ 基礎(chǔ)知識(shí)。這是該項(xiàng)目的視頻演示:我已經(jīng)使用KiCad為這個(gè)項(xiàng)目設(shè)計(jì)了 PCB 。我已將我的 IoT 小工具連接到我的一個(gè)燈架上,所以我沒(méi)有使用 Daniel 的桌面底座。代碼Github 上的小工具固件和 PlatformIO 項(xiàng)目
    發(fā)表于 06-17 10:03

    C++C/C++程序設(shè)計(jì)教程_C/C++概述

    C++基礎(chǔ)知識(shí),簡(jiǎn)要介紹了C++的一些簡(jiǎn)單知識(shí),概念,函數(shù)
    發(fā)表于 12-25 10:15 ?0次下載

    C++語(yǔ)言基礎(chǔ)知識(shí)講解

    C++語(yǔ)言基礎(chǔ)知識(shí)講解,喜歡的朋友可以下載來(lái)學(xué)習(xí)。
    發(fā)表于 01-14 15:30 ?22次下載

    Visual C++教程之C++基礎(chǔ)知識(shí)介紹

    本文檔的主要內(nèi)容詳細(xì)介紹的是Visual C++教程之C++基礎(chǔ)知識(shí)介紹主要內(nèi)容包括了:1 類(lèi)和對(duì)象,2 類(lèi)的成員及特性,3 繼承和派生類(lèi)
    發(fā)表于 02-15 15:59 ?9次下載
    Visual <b class='flag-5'>C++</b>教程之<b class='flag-5'>C++</b>的<b class='flag-5'>基礎(chǔ)知識(shí)</b>介紹

    C專(zhuān)家編程PDF電子書(shū)免費(fèi)下載

    內(nèi)容提要:《C專(zhuān)家編程》展示了最優(yōu)秀的C程序員所使用的編碼技巧,并專(zhuān)門(mén)開(kāi)辟了一章對(duì)C++基礎(chǔ)知識(shí)進(jìn)行了介紹。書(shū)中C的歷史、語(yǔ)言特性、聲明、
    發(fā)表于 05-14 08:00 ?16次下載
    <b class='flag-5'>C</b>專(zhuān)家編程PDF電子書(shū)免費(fèi)下載

    C++程序設(shè)計(jì)的基礎(chǔ)知識(shí)初步了解C++的資料免費(fèi)下載

    本文檔的主要內(nèi)容詳細(xì)介紹的是C++程序設(shè)計(jì)的基礎(chǔ)知識(shí)初步了解C++的資料免費(fèi)下載包括了:1 認(rèn)識(shí)C++,2 C++的現(xiàn)狀和發(fā)展,3
    發(fā)表于 06-10 08:00 ?25次下載
    <b class='flag-5'>C++</b>程序設(shè)計(jì)的<b class='flag-5'>基礎(chǔ)知識(shí)</b>初步了解<b class='flag-5'>C++</b>的資料免費(fèi)下載

    C++核心編程基礎(chǔ)知識(shí)大全免費(fèi)下載

    C++核心編程基礎(chǔ)知識(shí)大全免費(fèi)下載
    發(fā)表于 12-03 18:06 ?3次下載

    C/C++基礎(chǔ)知識(shí)匯總

    這是一篇五萬(wàn)字的C/C++知識(shí)點(diǎn)總結(jié),包括答案。
    的頭像 發(fā)表于 06-12 15:10 ?2820次閱讀

    C++基礎(chǔ)知識(shí)之面向?qū)ο笃?

    這兩期講完基本上面試遇到的相關(guān)問(wèn)題就過(guò)了一半了,后續(xù)將STL和內(nèi)存相關(guān)的補(bǔ)充完整,C++這塊的基本上就全部結(jié)束了,以后可能再也不會(huì)像現(xiàn)在這樣在這個(gè)方向投入過(guò)多時(shí)間,且行且珍惜啊,還是跟以前一樣,所有的總結(jié)都會(huì)有PDF版,如有需要自取。廢話不多說(shuō),發(fā)完這期,繼續(xù)整理STL去了。
    的頭像 發(fā)表于 03-02 09:49 ?547次閱讀

    C++基礎(chǔ)知識(shí)之面向?qū)ο笃?

    這兩期講完基本上面試遇到的相關(guān)問(wèn)題就過(guò)了一半了,后續(xù)將STL和內(nèi)存相關(guān)的補(bǔ)充完整,C++這塊的基本上就全部結(jié)束了,以后可能再也不會(huì)像現(xiàn)在這樣在這個(gè)方向投入過(guò)多時(shí)間,且行且珍惜啊,還是跟以前一樣,所有的總結(jié)都會(huì)有PDF版,如有需要自取。廢話不多說(shuō),發(fā)完這期,繼續(xù)整理STL去了。
    的頭像 發(fā)表于 03-02 09:53 ?587次閱讀

    C++基礎(chǔ)知識(shí)之函數(shù)1

    函數(shù)是 C++ 中的一個(gè)重要概念,它可以讓我們將一段代碼封裝起來(lái),然后在需要的時(shí)候調(diào)用它。C++ 中的函數(shù)有以下幾個(gè)特點(diǎn): * 函數(shù)可以有參數(shù)和返回值。 * 函數(shù)可以被其他函數(shù)調(diào)用。 * 函數(shù)可以被重載,即可以定義多個(gè)同名的函數(shù),只要它們的參數(shù)列表不同即可。
    的頭像 發(fā)表于 04-03 10:34 ?832次閱讀

    C++基礎(chǔ)知識(shí)之函數(shù)2

    C++中,我們可以使用inline關(guān)鍵字來(lái)定義內(nèi)聯(lián)函數(shù)。內(nèi)聯(lián)函數(shù)是一種特殊的函數(shù),它在編譯時(shí)會(huì)被直接嵌入到調(diào)用它的代碼中,從而避免了函數(shù)調(diào)用的開(kāi)銷(xiāo),提高了程序的執(zhí)行效率。內(nèi)聯(lián)函數(shù)的定義通常比較
    的頭像 發(fā)表于 04-03 10:34 ?766次閱讀

    C++語(yǔ)言基礎(chǔ)知識(shí)

    電子發(fā)燒友網(wǎng)站提供《C++語(yǔ)言基礎(chǔ)知識(shí).pdf》資料免費(fèi)下載
    發(fā)表于 07-19 10:58 ?10次下載