C++中的異常處理機(jī)制詳解
異常處理
增強(qiáng)錯(cuò)誤恢復(fù)能力是提高代碼健壯性的最有力的途徑之一,C語(yǔ)言中采用的錯(cuò)誤處理方法被認(rèn)為是緊耦合的,函數(shù)的使用者必須在非??拷瘮?shù)調(diào)用的地方編寫(xiě)錯(cuò)誤處理代碼,這樣會(huì)使得其變得笨拙和難以使用。C++中引入了異常處理機(jī)制,這是C++的主要特征之一,是考慮問(wèn)題和處理錯(cuò)誤的一種更好的方式。使用錯(cuò)誤處理可以帶來(lái)一些優(yōu)點(diǎn),如下:
錯(cuò)誤處理代碼的編寫(xiě)不再冗長(zhǎng)乏味,并且不再和正常的代碼混合在一起,程序員只需要編寫(xiě)希望產(chǎn)生的代碼,然后在后面某個(gè)單獨(dú)的區(qū)段里編寫(xiě)處理錯(cuò)誤的嗲嗎。多次調(diào)用同一個(gè)函數(shù),則只需要某個(gè)地方編寫(xiě)一次錯(cuò)誤處理代碼。
錯(cuò)誤不能被忽略,如果一個(gè)函數(shù)必須向調(diào)用者發(fā)送一次錯(cuò)誤信息。它將拋出一個(gè)描述這個(gè)錯(cuò)誤的對(duì)象。
傳統(tǒng)的錯(cuò)誤處理和異常處理
在討論異常處理之前,我們先談?wù)凜語(yǔ)言中的傳統(tǒng)錯(cuò)誤處理方法,這里列舉了如下三種:
在函數(shù)中返回錯(cuò)誤,函數(shù)會(huì)設(shè)置一個(gè)全局的錯(cuò)誤狀態(tài)標(biāo)志。
使用信號(hào)來(lái)做信號(hào)處理系統(tǒng),在函數(shù)中raise信號(hào),通過(guò)signal來(lái)設(shè)置信號(hào)處理函數(shù),這種方式耦合度非常高,而且不同的庫(kù)產(chǎn)生的信號(hào)值可能會(huì)發(fā)生沖突
使用標(biāo)準(zhǔn)C庫(kù)中的非局部跳轉(zhuǎn)函數(shù) setjmp和longjmp ,這里使用setjmp和longjmp來(lái)演示下如何進(jìn)行錯(cuò)誤處理:
#include
#include
jmp_buf static_buf; //用來(lái)存放處理器上下文,用于跳轉(zhuǎn)
void do_jmp()
{
//do something,simetime occurs a little error
//調(diào)用longjmp后,會(huì)載入static_buf的處理器信息,然后第二個(gè)參數(shù)作為返回點(diǎn)的setjmp這個(gè)函數(shù)的返回值
longjmp(static_buf,10);//10是錯(cuò)誤碼,根據(jù)這個(gè)錯(cuò)誤碼來(lái)進(jìn)行相應(yīng)的處理
}
int main()
{
int ret = 0;
//將處理器信息保存到static_buf中,并返回0,相當(dāng)于在這里做了一個(gè)標(biāo)記,后面可以跳轉(zhuǎn)過(guò)來(lái)
if((ret = setjmp(static_buf)) == 0) {
//要執(zhí)行的代碼
do_jmp();
} else { //出現(xiàn)了錯(cuò)誤
if (ret == 10)
std::cout << "a little error" << std::endl;
}
}
錯(cuò)誤處理方式看起來(lái)耦合度不是很高,正常代碼和錯(cuò)誤處理的代碼分離了,處理處理的代碼都匯聚在一起了。但是基于這種局部跳轉(zhuǎn)的方式來(lái)處理代碼,在C++中卻存在很嚴(yán)重的問(wèn)題,那就是對(duì)象不能被析構(gòu),局部跳轉(zhuǎn)后不會(huì)主動(dòng)去調(diào)用已經(jīng)實(shí)例化對(duì)象的析構(gòu)函數(shù)。這將導(dǎo)致內(nèi)存泄露的問(wèn)題。下面這個(gè)例子充分顯示了這點(diǎn)
#include
#include
using namespace std;
class base {
public:
base() {
cout << "base construct func call" << endl;
}
~base() {
cout << "~base destruct func call" << endl;
}
};
jmp_buf static_buf;
void test_base() {
base b;
//do something
longjmp(static_buf,47);//進(jìn)行了跳轉(zhuǎn),跳轉(zhuǎn)后會(huì)發(fā)現(xiàn)b無(wú)法析構(gòu)了
}
int main() {
if(setjmp(static_buf) == 0) {
cout << "deal with some thing" << endl;
test_base();
} else {
cout << "catch a error" << endl;
}
}
在上面這段代碼中,只有base類的構(gòu)造函數(shù)會(huì)被調(diào)用,當(dāng)longjmp發(fā)生了跳轉(zhuǎn)后,b這個(gè)實(shí)例將不會(huì)被析構(gòu)掉,但是執(zhí)行流已經(jīng)無(wú)法回到這里,b這個(gè)實(shí)例將不會(huì)被析構(gòu)。這就是局部跳轉(zhuǎn)用在C++中來(lái)處理錯(cuò)誤的時(shí)候帶來(lái)的一些問(wèn)題,在C++中異常則不會(huì)有這些問(wèn)題的存在。那么接下來(lái)看看如何定義一個(gè)異常,以及如何拋出一個(gè)異常和捕獲異常吧.
異常的拋出
class MyError {
const char* const data;
public:
MyError(const char* const msg = 0):data(msg)
{
//idle
}
};
void do_error() {
throw MyError("something bad happend");
}
int main()
{
do_error();
}
上面的例子中,通過(guò)throw拋出了一個(gè)異常類的實(shí)例,這個(gè)異常類,可以是任何一個(gè)自定義的類,通過(guò)實(shí)例化傳入的參數(shù)可以表明發(fā)生的錯(cuò)誤信息。其實(shí)異常就是一個(gè)帶有異常信息的類而已。異常被拋出后,需要被捕獲,從而可以從錯(cuò)誤中進(jìn)行恢復(fù),那么接下來(lái)看看如何去捕獲一個(gè)異常吧。在上面這個(gè)例子中使用拋出異常的方式來(lái)進(jìn)行錯(cuò)誤處理相比與之前使用局部跳轉(zhuǎn)的實(shí)現(xiàn)來(lái)說(shuō),最大的不同之處就是異常拋出的代碼塊中,對(duì)象會(huì)被析構(gòu),稱之為堆棧反解.
異常的捕獲
C++中通過(guò)catch關(guān)鍵字來(lái)捕獲異常,捕獲異常后可以對(duì)異常進(jìn)行處理,這個(gè)處理的語(yǔ)句塊稱為異常處理器。下面是一個(gè)簡(jiǎn)單的捕獲異常的例子:
try{
//do something
throw string("this is exception");
} catch(const string& e) {
cout << "catch a exception " << e << endl;
}
catch有點(diǎn)像函數(shù),可以有一個(gè)參數(shù),throw拋出的異常對(duì)象,將會(huì)作為參數(shù)傳遞給匹配到到catch,然后進(jìn)入異常處理器,上面的代碼僅僅是展示了拋出一種異常的情況,加入try語(yǔ)句塊中有可能會(huì)拋出多種異常的,那么該如何處理呢,這里是可以接多個(gè)catch語(yǔ)句塊的,這將導(dǎo)致引入另外一個(gè)問(wèn)題,那就是如何進(jìn)行匹配。
異常的匹配
異常的匹配我認(rèn)為是符合函數(shù)參數(shù)匹配的原則的,但是又有些不同,函數(shù)匹配的時(shí)候存在類型轉(zhuǎn)換,但是異常則不然,在匹配過(guò)程中不會(huì)做類型的轉(zhuǎn)換,下面的例子說(shuō)明了這個(gè)事實(shí):
#include
using namespace std;
int main()
{
try{
throw 'a';
}catch(int a) {
cout << "int" << endl;
}catch(char c) {
cout << "char" << endl;
}
}
上面的代碼的輸出結(jié)果是char,因?yàn)閽伋龅漠惓n愋途褪莄har,所以就匹配到了第二個(gè)異常處理器。可以發(fā)現(xiàn)在匹配過(guò)程中沒(méi)有發(fā)生類型的轉(zhuǎn)換。將char轉(zhuǎn)換為int。盡管異常處理器不做類型轉(zhuǎn)換,但是基類可以匹配到派生類這個(gè)在函數(shù)和異常匹配中都是有效的,但是需要注意catch的形參需要是引用類型或者是指針類型,否則會(huì) 導(dǎo)致切割派生類這個(gè)問(wèn)題。
//基類
class Base{
public:
Base(string msg):m_msg(msg)
{
}
virtual void what(){
cout << m_msg << endl;
}
void test()
{
cout << "I am a CBase" << endl;
}
protected:
string m_msg;
};
//派生類,重新實(shí)現(xiàn)了虛函數(shù)
class CBase : public Base
{
public:
CBase(string msg):Base(msg)
{
}
void what()
{
cout << "CBase:" << m_msg << endl;
}
};
int main()
{
try {
//do some thing
//拋出派生類對(duì)象
throw CBase("I am a CBase exception");
}catch(Base& e) { //使用基類可以接收
e.what();
}
}
上面的這段代碼可以正常的工作,實(shí)際上我們?nèi)粘>帉?xiě)自己的異常處理函數(shù)的時(shí)候也是通過(guò)繼承標(biāo)準(zhǔn)異常來(lái)實(shí)現(xiàn)字節(jié)的自定義異常的,但是如果將Base&換成Base的話,將會(huì)導(dǎo)致對(duì)象被切割,例如下面這段代碼將會(huì)編譯出錯(cuò),因?yàn)镃Base被切割了,導(dǎo)致CBase中的test函數(shù)無(wú)法被調(diào)用。
try {
//do some thing
throw CBase("I am a CBase exception");
}catch(Base e) {
e.test();
}
到此為此,異常的匹配算是說(shuō)清楚了,總結(jié)一下,異常匹配的時(shí)候基本上遵循下面幾條規(guī)則:
異常匹配除了必須要是嚴(yán)格的類型匹配外,還支持下面幾個(gè)類型轉(zhuǎn)換.
允許非常量到常量的類型轉(zhuǎn)換,也就是說(shuō)可以拋出一個(gè)非常量類型,然后使用catch捕捉對(duì)應(yīng)的常量類型版本
允許從派生類到基類的類型轉(zhuǎn)換
允許數(shù)組被轉(zhuǎn)換為數(shù)組指針,允許函數(shù)被轉(zhuǎn)換為函數(shù)指針
假想一種情況,當(dāng)我要實(shí)現(xiàn)一代代碼的時(shí)候,希望無(wú)論拋出什么類型的異常我都可以捕捉到,目前來(lái)說(shuō)我們只能寫(xiě)上一大堆的catch語(yǔ)句捕獲所有可能在代碼中出現(xiàn)的異常來(lái)解決這個(gè)問(wèn)題,很顯然這樣處理起來(lái)太過(guò)繁瑣,幸好C++提供了一種可以捕捉任何異常的機(jī)制,可以使用下列代碼中的語(yǔ)法。
catch(...) {
//異常處理器,這里可以捕捉任何異常,帶來(lái)的問(wèn)題就是無(wú)法或者異常信息
}
如果你要實(shí)現(xiàn)一個(gè)函數(shù)庫(kù),你捕捉了你的函數(shù)庫(kù)中的一些異常,但是你只是記錄日志,并不去處理這些異常,處理異常的事情會(huì)交給上層調(diào)用的代碼來(lái)處理.對(duì)于這樣的一個(gè)場(chǎng)景C++也提供了支持.
try{
throw Exception("I am a exception");
}catch(...) {
//log the exception
throw;
}
通過(guò)在catch語(yǔ)句塊中加入一個(gè)throw,就可以把當(dāng)前捕獲到的異常重新拋出.在異常拋出的那一節(jié)中,我在代碼中拋出了一個(gè)異常,但是我沒(méi)有使用任何catch語(yǔ)句來(lái)捕獲我拋出的這個(gè)異常,執(zhí)行上面的程序會(huì)出現(xiàn)下面的結(jié)果.
terminate called after throwing an instance of 'MyError'
Aborted (core dumped)
為什么會(huì)出現(xiàn)這樣的結(jié)果呢?,當(dāng)我們拋出一個(gè)異常的時(shí)候,異常會(huì)隨著函數(shù)調(diào)用關(guān)系,一級(jí)一級(jí)向上拋出,直到被捕獲才會(huì)停止,如果最終沒(méi)有被捕獲將會(huì)導(dǎo)致調(diào)用terminate函數(shù),上面的輸出就是自動(dòng)調(diào)用terminate函數(shù)導(dǎo)致的,為了保證更大的靈活性,C++提供了set_terminate函數(shù)可以用來(lái)設(shè)置自己的terminate函數(shù).設(shè)置完成后,拋出的異常如果沒(méi)有被捕獲就會(huì)被自定義的terminate函數(shù)進(jìn)行處理.下面是一個(gè)使用的例子:
#include
#include
#include
using namespace std;
class MyError {
const char* const data;
public:
MyError(const char* const msg = 0):data(msg)
{
//idle
}
};
void do_error() {
throw MyError("something bad happend");
}
//自定義的terminate函數(shù),函數(shù)原型需要一致
void terminator()
{
cout << "I'll be back" << endl;
exit(0);
}
int main()
{
//設(shè)置自定義的terminate,返回的是原有的terminate函數(shù)指針
void (*old_terminate)() = set_terminate(terminator);
do_error();
}
上面的代碼會(huì)輸出I'll be back
到此為此關(guān)于異常匹配的我所知道的知識(shí)點(diǎn)都已經(jīng)介紹完畢了,那么接著可以看看下一個(gè)話題,異常中的資源清理.
異常中的資源清理
在談到局部跳轉(zhuǎn)的時(shí)候,說(shuō)到局部調(diào)轉(zhuǎn)不會(huì)調(diào)用對(duì)象的析構(gòu)函數(shù),會(huì)導(dǎo)致內(nèi)存泄露的問(wèn)題,C++中的異常則不會(huì)有這個(gè)問(wèn)題,C++中通過(guò)堆棧反解將已經(jīng)定義的對(duì)象進(jìn)行析構(gòu),但是有一個(gè)例外就是構(gòu)造函數(shù)中如果出現(xiàn)了異常,那么這會(huì)導(dǎo)致已經(jīng)分配的資源無(wú)法回收,下面是一個(gè)構(gòu)造函數(shù)拋出異常的例子:
#include
#include
using namespace std;
class base
{
public:
base()
{
cout << "I start to construct" << endl;
if (count == 3) //構(gòu)造第四個(gè)的時(shí)候拋出異常
throw string("I am a error");
count++;
}
~base()
{
cout << "I will destruct " << endl;
}
private:
static int count;
};
int base::count = 0;
int main()
{
try{
base test[5];
} catch(...){
cout << "catch some error" << endl;
}
}
上面的代碼輸出結(jié)果是:
I start to construct I start to construct I start to construct I start to construct I will destruct I will destruct I will destruct catch some error
在上面的代碼中構(gòu)造函數(shù)發(fā)生了異常,導(dǎo)致對(duì)應(yīng)的析構(gòu)函數(shù)沒(méi)有執(zhí)行,因此實(shí)際編程過(guò)程中應(yīng)該避免在構(gòu)造函數(shù)中拋出異常,如果沒(méi)有辦法避免,那么一定要在構(gòu)造函數(shù)中對(duì)其進(jìn)行捕獲進(jìn)行處理.最后介紹一個(gè)知識(shí)點(diǎn)就是函數(shù)try語(yǔ)句塊,如果main函數(shù)可能會(huì)拋出異常該怎么捕獲?,如果構(gòu)造函數(shù)中的初始化列表可能會(huì)拋出異常該怎么捕獲?下面的兩個(gè)例子說(shuō)明了函數(shù)try語(yǔ)句塊的用法:
#include
using namespace std;
int main() try {
throw "main";
} catch(const char* msg) {
cout << msg << endl;
return 1;
}
main函數(shù)語(yǔ)句塊,可以捕獲main函數(shù)中拋出的異常.
class Base
{
public:
Base(int data,string str)try:m_int(data),m_string(str)//對(duì)初始化列表中可能會(huì)出現(xiàn)的異常也會(huì)進(jìn)行捕捉
{
// some initialize opt
}catch(const char* msg) {
cout << "catch a exception" << msg << endl;
}
private:
int m_int;
string m_string;
};
int main()
{
Base base(1,"zhangyifei");
}
上面說(shuō)了很多都是關(guān)于異常的使用,如何定義自己的異常,編寫(xiě)異常是否應(yīng)該遵循一定的標(biāo)準(zhǔn),在哪里使用異常,異常是否安全等等一系列的問(wèn)題,下面會(huì)一一討論的.
標(biāo)準(zhǔn)異常
C++標(biāo)準(zhǔn)庫(kù)給我們提供了一系列的標(biāo)準(zhǔn)異常,這些標(biāo)準(zhǔn)異常都是從exception類派生而來(lái),主要分為兩大派生類,一類是logic_error,另一類則是runtime_error這兩個(gè)類在stdexcept頭文件中,前者主要是描述程序中出現(xiàn)的邏輯錯(cuò)誤,例如傳遞了無(wú)效的參數(shù),后者指的是那些無(wú)法預(yù)料的事件所造成的錯(cuò)誤,例如硬件故障或內(nèi)存耗盡等,這兩者都提供了一個(gè)參數(shù)類型為std::string的構(gòu)造函數(shù),這樣就可以將異常信息保存起來(lái),然后通過(guò)what成員函數(shù)得到異常信息.
#include
#include
#include
using namespace std;
class MyError:public runtime_error {
public:
MyError(const string& msg = "") : runtime_error(msg) {}
};
//runtime_error logic_error 兩個(gè)都是繼承自標(biāo)準(zhǔn)異常,帶有string構(gòu)造函數(shù)
//
int main()
{
try {
throw MyError("my message");
} catch(MyError& x) {
cout << x.what() << endl;
}
}
異常規(guī)格說(shuō)明
假設(shè)一個(gè)項(xiàng)目中使用了一些第三方的庫(kù),那么第三方庫(kù)中的一些函數(shù)可能會(huì)拋出異常,但是我們不清楚,那么C++提供了一個(gè)語(yǔ)法,將一個(gè)函數(shù)可能會(huì)拋出的異常列出來(lái),這樣我們?cè)诰帉?xiě)代碼的時(shí)候參考函數(shù)的異常說(shuō)明即可,但是C++11中這中異常規(guī)格說(shuō)明的方案已經(jīng)被取消了,所以我不打算過(guò)多介紹,通過(guò)一個(gè)例子看看其基本用法即可,重點(diǎn)看看C++11中提供的異常說(shuō)明方案:
#include
#include
#include
#include
using namespace std;
class Up{};
class Fit{};
void g();
//異常規(guī)格說(shuō)明,f函數(shù)只能拋出Up 和Fit類型的異常
void f(int i)throw(Up,Fit) {
switch(i) {
case 1: throw Up();
case 2: throw Fit();
}
g();
}
void g() {throw 47;}
void my_ternminate() {
cout << "I am a ternminate" << endl;
exit(0);
}
void my_unexpected() {
cout << "unexpected exception thrown" << endl;
// throw Up();
throw 8;
//如果在unexpected中繼續(xù)拋出異常,拋出的是規(guī)格說(shuō)明中的 則會(huì)被捕捉程序繼續(xù)執(zhí)行
//如果拋出的異常不在異常規(guī)格說(shuō)明中分兩種情況
//1.異常規(guī)格說(shuō)明中有bad_exception ,那么會(huì)導(dǎo)致拋出一個(gè)bad_exception
//2.異常規(guī)格說(shuō)明中沒(méi)有bad_exception 那么會(huì)導(dǎo)致程序調(diào)用ternminate函數(shù)
// exit(0);
}
int main() {
set_terminate(my_ternminate);
set_unexpected(my_unexpected);
for(int i = 1;i <=3;i++)
{
//當(dāng)拋出的異常,并不是異常規(guī)格說(shuō)明中的異常時(shí)
//會(huì)導(dǎo)致最終調(diào)用系統(tǒng)的unexpected函數(shù),通過(guò)set_unexpected可以
//用來(lái)設(shè)置自己的unexpected汗函數(shù)
try {
f(i);
}catch(Up) {
cout << "Up caught" << endl;
}catch(Fit) {
cout << "Fit caught" << endl;
}catch(bad_exception) {
cout << "bad exception" << endl;
}
}
}
}
上面的代碼說(shuō)明了異常規(guī)格說(shuō)明的基本語(yǔ)法,以及unexpected函數(shù)的作用,以及如何自定義自己的unexpected函數(shù),還討論了在unexpected函數(shù)中繼續(xù)拋出異常的情況下,該如何處理拋出的異常.C++11中取消了這種異常規(guī)格說(shuō)明.引入了一個(gè)noexcept函數(shù),用于表明這個(gè)函數(shù)是否會(huì)拋出異常
void recoup(int) noexecpt(true); //recoup不會(huì)拋出異常 void recoup(int) noexecpt(false); //recoup可能會(huì)拋出異常
此外還提供了noexecpt用來(lái)檢測(cè)一個(gè)函數(shù)是否不拋出異常.
異常安全
異常安全我覺(jué)得是一個(gè)挺復(fù)雜的點(diǎn),不光光需要實(shí)現(xiàn)函數(shù)的功能,還要保存函數(shù)不會(huì)在拋出異常的情況下,出現(xiàn)不一致的狀態(tài).這里舉一個(gè)例子,大家在實(shí)現(xiàn)堆棧的時(shí)候經(jīng)??吹綍?shū)中的例子都是定義了一個(gè)top函數(shù)用來(lái)獲得棧頂元素,還有一個(gè)返回值是void的pop函數(shù)僅僅只是把棧頂元素彈出,那么為什么沒(méi)有一個(gè)pop函數(shù)可以 即彈出棧頂元素,并且還可以獲得棧頂元素呢?
template<typename T> T stack<T>::pop()
{
if(count == 0)
throw logic_error("stack underflow");
else
return data[--count];
}
如果函數(shù)在最后一行拋出了一個(gè)異常,那么這導(dǎo)致了函數(shù)沒(méi)有將退棧的元素返回,但是Count已經(jīng)減1了,所以函數(shù)希望得到的棧頂元素丟失了.本質(zhì)原因是因?yàn)檫@個(gè)函數(shù)試圖一次做兩件事,1.返回值,2.改變堆棧的狀態(tài).最好將這兩個(gè)獨(dú)立的動(dòng)作放到兩個(gè)獨(dú)立的函數(shù)中,遵守內(nèi)聚設(shè)計(jì)的原則,每一個(gè)函數(shù)只做一件事.我們 再來(lái)討論另外一個(gè)異常安全的問(wèn)題,就是很常見(jiàn)的賦值操作符的寫(xiě)法,如何保證賦值操作是異常安全的.
class Bitmap {...};
class Widget {
...
private:
Bitmap *pb;
};
Widget& Widget::operator=(const Widget& rhs)
{
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
上面的代碼不具備自我賦值安全性,倘若rhs就是對(duì)象本身,那么將會(huì)導(dǎo)致*rhs.pb指向一個(gè)被刪除了的對(duì)象.那么就緒改進(jìn)下.加入證同性測(cè)試.
Widget& Widget::operator=(const Widget& rhs)
{
If(this == rhs) return *this; //證同性測(cè)試
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
但是現(xiàn)在上面的代碼依舊不符合異常安全性,因?yàn)槿绻鹍elete pb執(zhí)行完成后在執(zhí)行new Bitmap的時(shí)候出現(xiàn)了異常,則會(huì)導(dǎo)致最終指向一塊被刪除的內(nèi)存.現(xiàn)在只要稍微改變一下,就可以讓上面的代碼具備異常安全性.
Widget& Widget::operator=(const Widget& rhs)
{
If(this == rhs) return *this; //證同性測(cè)試
Bitmap *pOrig = pb;
pb = new Bitmap(*rhs.pb); //現(xiàn)在這里即使發(fā)生了異常,也不會(huì)影響this指向的對(duì)象
delete pOrig;
return *this;
}
這個(gè)例子看起來(lái)還是比較簡(jiǎn)單的,但是用處還是很大的,對(duì)于賦值操作符來(lái)說(shuō),很多情況都是需要重載的.
相關(guān)文章
教你如何使用qt quick-PathView實(shí)現(xiàn)好看的home界面
pathView的使用類似與ListView,都需要模型(model)和代理(delegate),只不過(guò)pathView多了一個(gè)路徑(path)屬性,顧名思義路徑就是item滑動(dòng)的路徑,下面給大家分享qt quick-PathView實(shí)現(xiàn)好看的home界面,一起看看吧2021-06-06
C++宏函數(shù)和內(nèi)聯(lián)函數(shù)的使用
本文主要介紹了C++宏函數(shù)和內(nèi)聯(lián)函數(shù)的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07
詳解C語(yǔ)言中sizeof如何在自定義函數(shù)中正常工作
在main函數(shù)中,sizeof是可以正常工作的,但是在自定義函數(shù)中就不可以了。所以本文將為大家詳細(xì)講解一下如何解決這一問(wèn)題,感興趣的可以了解一下2022-05-05
在Centos7中使用vscode和gdb調(diào)試PG插件的方法
這篇文章主要介紹了在Centos7中使用vscode和gdb調(diào)試PG插件,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-09-09
C++中使用哈希表(unordered_map)的一些常用操作方法
C++標(biāo)準(zhǔn)庫(kù)中使用的unordered_map底層實(shí)現(xiàn)是哈希表,下面這篇文章主要給大家介紹了關(guān)于C++中使用哈希表(unordered_map)的一些常用操作方法,需要的朋友可以參考下2022-03-03
C語(yǔ)言解決堆棧括號(hào)匹配問(wèn)題示例詳解
這篇文章主要為大家介紹了C語(yǔ)言堆棧括號(hào)匹配問(wèn)題示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2021-11-11
c語(yǔ)言string.h頭文件中所有函數(shù)示例詳解
這篇文章詳細(xì)介紹了C語(yǔ)言標(biāo)準(zhǔn)庫(kù)中的字符串和內(nèi)存操作函數(shù),以str開(kāi)頭的字符串處理函數(shù)和以mem開(kāi)頭的內(nèi)存處理函數(shù),每種函數(shù)都有詳細(xì)的原型、功能描述和示例代碼,需要的朋友可以參考下2024-11-11
C語(yǔ)言用數(shù)組實(shí)現(xiàn)反彈球消磚塊
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言用數(shù)組實(shí)現(xiàn)反彈球消磚塊,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05

