C++-运行时类型信息,异常(day11)
一、運(yùn)行時(shí)類型信息
1、typeid運(yùn)算符
頭文件:#include<typeinfo>
C++的標(biāo)準(zhǔn)頭文件,都對應(yīng)相應(yīng)的類
//sizeof(類型/變量/表達(dá)式),返回內(nèi)存大小
typeid(類型/變量/表達(dá)式),返回typeinfo類型的對象,其中包含name()成員函數(shù),返回字符串,描述類型信息
雖然與函數(shù)調(diào)用形式相同,但是typeid是操作符。
?
int x;cout<<typeid(int).name()<<endl;//不同編譯器的字符串描述可能不同cout<<typeid(x).name()<<endl;cout<<typeid(int* [5]).name()<<endl;
cout<<typeid(int(*) [5]).name()<<endl;//數(shù)組指針
?
class X{protected:virtual void foo(void){} };class Y{protected:void foo(void){} }class Z{void foo(void){}};void func(X& x){/*if(!strcmp(typeid(x).name(),"1Y")){cout<<"Y"<<endl;}else if(!strcmp(typeid(x).name(),"1Z")){cout<<"Z"<<endl;}else(!strcmp(typeid(x).name(),"1X")){cout<<"X"<<endl;}*/ //typeinfo類中已經(jīng)重載的operator==函數(shù)if(!strcmp(typeid(x).==typeid(Y))){cout<<"Y"<<endl;}else if(!strcmp(typeid(x)==typeid(Z))){cout<<"Z"<<endl;}else(!strcmp(typeid(x)==typeid(X)){cout<<"X"<<endl;}}?
注意:
typeid的使用是基于基類中存在虛函數(shù),并且子類繼承并覆蓋了虛函數(shù)。否則typeid無法獲取類型信息。
?
2、動態(tài)類型轉(zhuǎn)換運(yùn)算符(day3)
目標(biāo)類型變量=dynamic_cast<目標(biāo)類型>目標(biāo)源類型變量
使用場景:適用于具有多態(tài)繼承關(guān)系的父子類指針或者引用之間的顯式轉(zhuǎn)換。
?
class A{virtual void foo(void){}};class B:publicA{void foo(void)};class C:publicA{void foo(void)};class D{};B b;A* pa=&b;//B* pb=pa;//向下轉(zhuǎn)換,編譯報(bào)錯(cuò)//B* pb=static_cast<B*>(pa);//ok B* pb=dynamic_cast<B*>(pa);//ok//C* pc=static_cast<C*>(pa);//因?yàn)閜a已經(jīng)指向了B*的類型,雖然可以通過,但是不安全 C* pc=dynamic_cast<C*>(pa);//程序執(zhí)行階段進(jìn)行轉(zhuǎn)換,而靜態(tài)轉(zhuǎn)換是程序編譯階段進(jìn)行轉(zhuǎn)換,雖然不報(bào)錯(cuò),但是pc的地址將為空。執(zhí)行時(shí)動態(tài)類型轉(zhuǎn)換會做類型檢查,如果是無關(guān)的類,無法轉(zhuǎn)換。 D* pc=dynamic_cast<D*>(pa);//也為空//可以打印出pa,pb,pc的地址,可見pc為空地址//使用引用的不合理類型動態(tài)轉(zhuǎn)換時(shí),執(zhí)行時(shí)會進(jìn)程會被終止?
?
dynamic_cast在轉(zhuǎn)換的過程中,會根據(jù)多態(tài)的特性,檢查父子類的指針或者引用目標(biāo)類型是否一致,如果一致則轉(zhuǎn)換成功,否則轉(zhuǎn)換失敗,如果是指針轉(zhuǎn)換,則返回NULL,如果是引用轉(zhuǎn)換,則拋出異?!癰ad_cast”
?
?
?
二、異常(Exception)
1、常見錯(cuò)誤
1)語法錯(cuò)誤
2)邏輯錯(cuò)誤
3)功能錯(cuò)誤
4)設(shè)計(jì)缺陷
5)需求不符
6)環(huán)境異常
7)操作不當(dāng)
?
2、C的錯(cuò)誤處理機(jī)制
1)通過返回值表示錯(cuò)誤
class A{public:A(void){cout<<"A::A()"<<endl;}~A(void){cout<<"~A::A()"<<endl;}};//通過返回值表示錯(cuò)誤int func3(void){A a;FILE* fp=fopen("none.text","r");if(fp==NULL){cout<<"file open error!"<<endl;return -1;}fclose(fp)return 0;}int func2(void){A a;if(func3()==-1){return -1;}return 0;}int func1(void){A a;if(func2()==-1){return -1;}//... retuern 0;}int main(void){if(func1()==-1){return -1;}//...return 0;}//通過返回值表示錯(cuò)誤 棧區(qū)對象在出現(xiàn)異常之后,能夠正常釋放?
2)通過遠(yuǎn)程跳轉(zhuǎn)來處理錯(cuò)誤
?
jmp_buf g_env;//包含頭文件#include<setjmp.h>
class A{public:A(void){cout<<"A::A()"<<endl;}~A(void){cout<<"~A::A()"<<endl;}};//通過返回值表示錯(cuò)誤int func3(void){A a;FILE* fp=fopen("none.text","r");if(fp==NULL){longjmp(g_env,-1);}//...fclose(fp)return 0;}int func2(void){A a;func3();return 0;}int func1(void){A a;func2();//...retuern 0;}int main(void){if(setjmp(g_nev)==-1){//先設(shè)置g_env,如果有錯(cuò)誤,會再次直接跳轉(zhuǎn)到此處
cout<<"file open error!"<<endl;
}
func1();//...return 0;} ?
?
使用遠(yuǎn)程跳轉(zhuǎn)棧區(qū)對象無法得到釋放
?
?
?通過返回值表示錯(cuò)誤
優(yōu)點(diǎn):函數(shù)調(diào)用路徑中所有的局部對象都能夠得到正常的析構(gòu),不會內(nèi)存泄漏。
缺點(diǎn):錯(cuò)誤處理流程比較復(fù)雜,逐層判斷,代碼臃腫
通過鹽城跳轉(zhuǎn)機(jī)制處理錯(cuò)誤
優(yōu)點(diǎn):不需要逐層判斷,實(shí)現(xiàn)一步到位的錯(cuò)誤處理,代碼精簡
缺點(diǎn):函數(shù)調(diào)用的路徑中布局對象失去被析構(gòu)的機(jī)會,形成內(nèi)存析構(gòu)
?
?3、C++的異常處理機(jī)制
? 結(jié)合C中兩種錯(cuò)誤處理的優(yōu)點(diǎn),同時(shí)避免他們的缺點(diǎn),在形式上實(shí)現(xiàn)一步到位的錯(cuò)誤處理,無需逐層判斷返回值,所有的局部對象得到正常的析構(gòu)。
class A{public:A(void){cout<<"A::A()"<<endl;}~A(void){cout<<"~A::A()"<<endl;}};//通過返回值表示錯(cuò)誤int func3(void){A a;FILE* fp=fopen("none.text","r");if(fp==NULL){throw -1;//拋出異常 }//... fclose(fp)return 0;}int func2(void){A a;func3();return 0;}int func1(void){A a;func2();//... retuern 0;}int main(void){try{func1();//...出現(xiàn)異常,此處將不會得到執(zhí)行,直接跳轉(zhuǎn)到catch }catch(int ex/*拋出的異常數(shù)據(jù)類型*/){cout<<"file open error!"<<endl;return -1;}//...return 0;}?
如果執(zhí)行到throw語句,會逐層返回執(zhí)行},并且內(nèi)存會得到釋放?
4、C++異常語法
(1)異常拋出
throw 異常對象;//拋出的異常會被放到安全區(qū),無法手動訪問
如:
throw -1;
throw "file error";
throw 對象;
(2)異常捕獲
try{
//可能引發(fā)異常的語句
}
catch(異常類型1){
//異常類型1的處理
}
catch(異常類型2){
//異常類型2的處理
}
...
catch(.../*可以匹配任意類型*/){
//針對其他類型的處理
}
class FileError{ public:FileError(){}FileError(const string& file,int line):m_file(file),m_line(line){cout<<"拋出位置"<<m_file<<","<<m_line;} private:string m_file;int m_line; }; class A{public:A(void){cout<<"A::A()"<<endl;}~A(void){cout<<"~A::A()"<<endl;}};//通過返回值表示錯(cuò)誤int func3(void){A a;FILE* fp=fopen("none.text","r");if(fp==NULL){throw FileError(__FILE__,__LINE__);/*注意,是雙下劃線,而不是單下劃線__FILE__ 包含當(dāng)前程序文件名的字符串__LINE__ 表示當(dāng)前行號的整數(shù)__DATE__ 包含當(dāng)前日期的字符串__STDC__ 如果編譯器遵循ANSI C標(biāo)準(zhǔn),它就是個(gè)非零值__TIME__ 包含當(dāng)前時(shí)間的字符串*///FileError ex;//throw ex;throw "file error";throw -1;//拋出異常 }//... fclose(fp)return 0;}int func2(void){A a;func3();return 0;}int func1(void){A a;func2();//... retuern 0;}int main(void){try{func1();//...出現(xiàn)異常,此處將不會得到執(zhí)行,直接跳轉(zhuǎn)到catch }catch(int ex/*拋出的異常數(shù)據(jù)類型*/){cout<<"file open error!"<<endl;return -1;}catch(const char* ex){cout<<"file error!"<<endl;return -1;}catch(FileError& ex){//如果拋出的是對象,最好是用引用cout<<"FileError"<<endl;return -1;}//...return 0;}
?
注意:
(1)如果沒有類型可以匹配,那么拋出的異常將被系統(tǒng)鎖捕獲,進(jìn)程將被回收。內(nèi)存被釋放
(2)如果有兩個(gè)連續(xù)的throw語句,只會被執(zhí)行一個(gè),因?yàn)樵趖hrow時(shí)候,直接跳轉(zhuǎn)到}執(zhí)行。
(3)如果拋出的是類的對象,最好使用引用。
(4)在面向?qū)ο蟮木幊讨?#xff0c;一般都是拋出對象,而不是整數(shù)或者字符串等基本類型,因?yàn)轭惐然绢愋涂梢源鎯Ω嘈畔?。比如日志等?/p>
?
5、異常-擴(kuò)展
class A{};class B:public A{};void func(void){//...throw(B);//throw(A); }int main(void){try{func();}catch(A& ex){//向上造型可以匹配B類異常 cout<<"捕獲到A類異常"<<endl;return -1;}catch(B& ex){cout<<"捕獲到B類的異常"<<endl;return -1;}return 0;}//上述代碼中B異常將無法被捕獲,無論是拋A,還是拋B,正確的處理方式應(yīng)該把子類的異常捕獲放在基類之前,防止發(fā)生向上造型。?
注意:
catch的匹配是自上而下進(jìn)行匹配,而不是選擇最優(yōu)匹配,所以應(yīng)該把子類的異常捕獲放在基類之前,防止發(fā)生向上造型。
?
?
6、異常說明1)可以在函數(shù)原型中增加異常說明,說明該函數(shù)可能拋出的異常類型。提前通知編譯器,函數(shù)會拋出的異常類型
返回類型 函數(shù)名(形參表)[cosnt]throw(異常類型表){...}
不加異常說明列表,異常也能夠被正常捕獲,和不加的區(qū)別在于,如果函數(shù)拋出了與說明列表不符的類型,這個(gè)異常將不會被捕獲。自然也會被系統(tǒng)所捕獲。
2)函數(shù)的異常說明只是一種承諾,表示該函數(shù)不會拋出說明列表意外的類型。意外的異常將會被系統(tǒng)所捕獲。
3)如果不寫異常說明,表示可以拋出任何異常
4)空異常說明,throw(),表示不會拋出任何異常。
5)如果函數(shù)的聲明和定義分開,在聲明和定義部分都要加上異常說明。并且說明列表必須相同,但是順序可以改變。
7、異常說明與多態(tài) class FileError{}; class MemError{};class Base{ public:virtual void func(void)throw(FileError,MemError){} };class Derived:public Base{ public:void func(void){}//虛函數(shù)覆蓋會失敗,因?yàn)樽宇惖奶摵瘮?shù)覆蓋函數(shù)沒有異常說明,這里異常說明范圍可以縮小,但是不能擴(kuò)大 };
如果基類中的虛函數(shù)帶有異常說明,它的子類中,該函數(shù)的覆蓋版本不能比基類版本拋出更多異常,否則編譯器報(bào)出“放松throw限定”錯(cuò)誤
?
轉(zhuǎn)載于:https://www.cnblogs.com/ptfe/p/11300823.html
與50位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的C++-运行时类型信息,异常(day11)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: step4 . day4 库函数和库函数
- 下一篇: 计算是几维数组