第十週 C++11新特性和C++高級主題
1.C++11新特性(一)
2.C++11新特性(二)
3.強制類型轉換
4.異常處理
4.異常處理,課程最後一節
程序運行發生異常
程序運行中總難免發生錯誤
(1)數組元素的下標超界、訪問NULL指針
(2)除數爲0
(3)動態內存分配new需要的存儲空間太大
……
引起這些異常情況的原因可能是:
(1)代碼質量不高,存在BUG
(2)輸入數據不符合要求
(3)程序的算法設計時考慮不周到
…….
發生異常怎麼辦
(1)不只是簡單地終止程序運行
(2)能夠反饋異常情況的信息:哪一段代碼發生的、什麼異常
(3)能夠對程序運行中已發生的事情做些處理:取消對輸入文件的改動、釋放已經申請的系統資源
……
通常的做法是:在預計會發生異常的地方,加入相應的代碼,但這種做法並不總是適用的
……//對文件A進行了相關的操作。
fun(arg, ……);//可能發生異常
……
調用者該如何知道fun(arg, …)是否發生異常?沒有發生異常,可以繼續執行;發生異常,應該在結束程序運行前還原對文件A的操作。但是,
fun(arg, …)是別人已經開發好的代碼
fun(arg, …)的編寫者不知道其他人會如何使用這個函數
fun(arg, …)會出現在表達式中,通過返回值的方式區分是否發生異常,不符合編寫程序的習慣,而且可能發生多種異常,通過返回值判斷也很麻煩。
那麼,需要一種手段:(1)把異常與函數的接口分開,並且能夠區分不同的異常;(2)在函數體外捕獲所發生的異常,並提供更多的異常信息.
異常處理
一個函數運行期間可能產生異常。在函數內部對異常進行處理未必合適。因爲函數設計者無法知道函數調用者希望如何處理異常。
告知函數調用者發生了異常,讓函數調用者處理比較好
用函數返回值告知異常不方便
用try、catch進行異常處理
try塊包含有可能產生異常的語句,如果遇到異常就用throw拋出異常,用類型表示異常類型。一旦遇到異常,throw拋出後就跳出try塊,進入catch塊。一個try塊後可以跟很多個catch塊,但是catch塊只有一個會被真的執行,因爲遇到異常就結束try塊了,異常只有一個。如果try塊沒有拋出異常,就不會進入任何catch塊。catch塊捕獲任何類型的異常,參數匹配try塊中throw出的數據類型,catch(…)捕獲任何類型的異常,可以作爲最後一個catch塊,防止有異常類型沒有被捕獲。
#include <iostream>
using namespace std;
int main()
{
double m ,n;
cin >> m >> n;
try {
cout << "before dividing." << endl;
if( n == 0)
throw -1; //拋出int類型異常
else
cout << m / n << endl;
cout << "after dividing." << endl;
}
catch(double d) {
cout << "catch(double) " << d << endl;
}
catch(int e) {
cout << "catch(int) " << e << endl;
}
cout << "finished" << endl;
return 0;
}
程序運行結果如下:
9 6↙
before dividing.
1.5
after dividing.
finished
#include <iostream>
using namespace std;
int main()
{
double m ,n;
cin >> m >> n;
try {
cout << "before dividing." << endl;
if( n == 0)
throw -1; //拋出整型異常
else if( m == 0 )
throw -1.0; //拋出double型異常
else
cout << m / n << endl;
cout << "after dividing." << endl;
}
catch(double d) {
cout << "catch(double) " << d << endl;
}
catch(...) {
cout << "catch(...) " << endl;
}
cout << "finished" << endl;
return 0;
}
程序運行結果:
9 0↙
before dividing.
catch(...)
finished
0 6↙
before dividing.
catch(double) -1
finished
異常的再拋出
如果一個函數在執行的過程中,拋出的異常在本函數內就被catch塊捕獲並處理了,那麼該異常就不會拋給這個函數的調用者(也稱“上一層的函數”);如果異常在本函數中沒被處理,就會被拋給上一層的函數。
#include <iostream>
#include <string>
using namespace std;
class CException
{
public :
string msg;
CException(string s):msg(s) { }
};
double Devide(double x, double y)
{
if(y == 0)
throw CException("devided by zero");//這個異常就要被返回到上一層函數,讓程序員看到
cout << "in Devide" << endl;
return x / y;
}
int CountTax(int salary)
{
try {
if( salary < 0 )
throw -1;
cout << "counting tax" << endl;
}
catch (int ) {//異常在函數內部就被處理掉了
cout << "salary < 0" << endl;
}
cout << "tax counted" << endl;
return salary * 0.15;
}
int main()
{
double f = 1.2;
try {
CountTax(-1);
f = Devide(3,0);//Devide函數內部遇到異常,直接跳出,賦值語句不會被執行
cout << "end of try block" << endl;
}
catch(CException e) {
cout << e.msg << endl;
}
cout << "f=" << f << endl;
cout << "finished" << endl;
return 0;
}
輸出結果:
salary < 0
tax counted
devided by zero
f=1.2
finished
C++標準異常類
C++標準庫中有一些類代表異常,這些類都是從exception類派生而來
bad_cast
在用 dynamic_cast進行從多態基類對象(或引用),到派生類的引用的強制類型轉換時,如果轉換是不安全的,則會拋出此異常。
#include <iostream>
#include <stdexcept>
#include <typeinfo>
using namespace std;
class Base
{
virtual void func(){}
};
class Derived : public Base
{
public:
void Print() { }
};
void PrintObj( Base & b)
{
try {
Derived & rd = dynamic_cast<Derived&>(b);//此轉換若不安全,會拋出bad_cast異常
rd.Print();
}
catch (bad_cast& e) {
cerr << e.what() << endl;
}
}
int main ()
{
Base b;
PrintObj(b);
return 0;
}
輸出結果:
Bad dynamic_cast!
bad_alloc
在用new運算符進行動態內存分配時,如果沒有足夠的內存,則會引發此異常。
#include <iostream>
#include <stdexcept>
using namespace std;
int main ()
{
try {
char * p = new char[0x7fffffff];//無法分配這麼多空間,會拋出異常
}
catch (bad_alloc & e) {
cerr << e.what() << endl;
}
return 0;
}
輸出結果:
bad allocation
out_of_range
用vector或string的at成員函數根據下標訪問元素時,如果下標越界,就會拋出此異常。[]也可以根據下標訪問元素,速度比at成員函數快,但是遇到越界時不會拋出異常。例如:
#include <iostream>
#include <stdexcept>
#include <vector>
#include <string>
using namespace std;
int main ()
{
vector<int> v(10);
try {
v.at(100)=100; //拋出out_of_range異常
}
catch (out_of_range& e) {
cerr << e.what() << endl;
}
string s = "hello";
try {
char c = s.at(100); //拋出out_of_range異常
}
catch (out_of_range& e) {
cerr << e.what() << endl;
}
return 0;
}
輸出結果:
invalid vector<T> subscript
invalid string position
運行時類型檢查
C++運算符typeid是單目運算符,可以在程序運行過程中獲取一個表達式的值的類型。typeid運算的返回值是一個type_info類的對象,裏面包含了類型的信息。
typeid和type_info用法示例
#include <iostream>
#include <typeinfo> //要使用typeinfo,需要此頭文件
using namespace std;
struct Base { }; //非多態基類
struct Derived : Base { };
struct Poly_Base {virtual void Func(){ } }; //多態基類
struct Poly_Derived: Poly_Base { };
int main()
{
//基本類型
long i; int * p = NULL;
cout << "1) int is: " << typeid(int).name() << endl;
//輸出 1) int is: int
cout << "2) i is: " << typeid(i).name() << endl;
//輸出 2) i is: long
cout << "3) p is: " << typeid(p).name() << endl;
//輸出 3) p is: int *
cout << "4) *p is: " << typeid(*p).name() << endl ;
//輸出 4) *p is: int
//非多態類型
Derived derived;
Base* pbase = &derived;
cout << "5) derived is: " << typeid(derived).name() << endl;
//輸出 5) derived is: struct Derived
cout << "6) *pbase is: " << typeid(*pbase).name() << endl;
//輸出 6) *pbase is: struct Base
cout << "7) " << (typeid(derived)==typeid(*pbase) ) << endl;
//輸出 7) 0
//多態類型
Poly_Derived polyderived;
Poly_Base* ppolybase = &polyderived;
cout << "8) polyderived is: " << typeid(polyderived).name() << endl;
//輸出 8) polyderived is: struct Poly_Derived
cout << "9) *ppolybase is: " << typeid(*ppolybase).name() << endl;
//輸出 9) *ppolybase is: struct Poly_Derived
cout << "10) " << (typeid(polyderived)!=typeid(*ppolybase) ) << endl;
//輸出 10) 0
}