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主要有如下幾種用法:
-
用于類(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)的成員變量賦值。 -
用于基本數(shù)據(jù)類(lèi)型之間的轉(zhuǎn)換,如int、float、char之間的互相轉(zhuǎn)換
-
把空指針轉(zhuǎn)換成 目標(biāo)類(lèi)型的空指針 。
-
把任何類(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ū)別
- 指針有自己的一塊空間,而引用只是一個(gè)別名;
- 使用 sizeof 看一個(gè)指針的大小是 4,而引用則是被引用對(duì)象的大小;
- 指針可以被初始化為 NULL,而引用必須被初始化且必須是一個(gè)已有對(duì)象的引用;
- 作為參數(shù)傳遞時(shí),指針需要被解引用才可以對(duì)對(duì)象進(jìn)行操作,而對(duì)引用的修改都會(huì)改變引用所指向的對(duì)象;
- 可以有 const 指針,但是沒(méi)有 const 引用;
- 指針在使用中可以指向其它對(duì)象,但是引用只能是一個(gè)對(duì)象的引用,不能 被改變;
- 指針可以有多級(jí)指針(**p),而引用至于一級(jí);
- 指針和引用使用++運(yùn)算符的意義不一樣;
- 如果返回動(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 namesp
ace 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)順序
- 派生類(lèi)本身的析構(gòu)函數(shù)
- 對(duì)象成員析構(gòu)函數(shù)
- 基類(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ū)六部分。
- 棧(stack) :程序 自動(dòng)分配 ,使用??臻g存儲(chǔ)函數(shù)的返回地址、參數(shù)、局部變量、返回值。
- 堆(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ū)。
- 靜態(tài)/全局區(qū) :在 C++ 里面沒(méi)有區(qū)分bss和data。
- bss段 :存儲(chǔ)未初始化的全局變量和靜態(tài)變量(局部+全局),以及所有被初始化為0的全局變量和靜態(tài)變量,Block Started by Symbol。
- data段 :存儲(chǔ)程序中已初始化的全局變量和靜態(tài)變量。
- 代碼區(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ū)域
- 局部常量 ,存放在 棧區(qū) ;
- static/全局常量 ,存放在 靜態(tài)/全局存儲(chǔ)區(qū) ;
- 字面值常量 ,其值一望而知,存放在 常量區(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 +=1;
return *this;
}
i++ 實(shí)現(xiàn):
const int int::operator(int)
{
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;
}
審核編輯:湯梓紅
-
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
發(fā)布評(píng)論請(qǐng)先 登錄
【微信精選】C++的精髓類(lèi)和繼承就該這么理解!
【微信精選】C++的精髓類(lèi)和繼承就該這么理解!
分享一個(gè)開(kāi)源的ESP32 物聯(lián)網(wǎng)小工具
C++C/C++程序設(shè)計(jì)教程_C/C++概述
C++語(yǔ)言基礎(chǔ)知識(shí)講解
Visual C++教程之C++的基礎(chǔ)知識(shí)介紹

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

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

評(píng)論