中国大学MOOCプログラム設計とアルゴリズム(三):C++対象向けプログラム設計第十週C++11新特性とC++高級テーマノートの異常処理

35744 ワード

第10週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)関数体外で発生する異常を捕捉し、より多くの異常情報を提供する.
例外処理
1つの関数の実行中に異常が発生する可能性があります.関数内部で異常を処理するのは必ずしも適切ではない.関数デザイナーは、関数呼び出し者が例外をどのように処理するか分からないためです.通知関数呼び出し者に異常が発生したことを通知し、関数呼び出し者に処理させたほうが関数戻り値で異常を通知するのは不便である.
try、catchによる異常処理
tryブロックには異常が発生する可能性のある文が含まれており、異常に遭遇した場合はthrowで異常を投げ出し、タイプで異常タイプを表す.異常に遭遇するとthrowが投げ出されるとtryブロックから飛び出しcatchブロックに入ります.1つのtryブロックの後に多くのcatchブロックと付き合うことができますが、catchブロックは1つだけ本当に実行されます.異常が発生するとtryブロックが終了し、異常は1つしかありません.tryブロックに異常が投げ出されていない場合、catchブロックは入りません.catchブロックは任意のタイプの異常をキャプチャし、パラメータはtryブロックのthrowから出たデータ型に一致し、catch(...)は任意のタイプの異常をキャプチャし、最後のcatchブロックとして、異常タイプがキャプチャされないことを防止することができる.
#include 
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 
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


異常な再放出
1つの関数が実行中に投げ出された異常が本関数内でcatchブロックによってキャプチャされて処理された場合、この異常はこの関数の呼び出し者(「上位の関数」とも呼ばれる)に投げ出されません.異常が本関数で処理されていなければ,上の層の関数に投げ込まれる.
#include 
#include 
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 
#include 
#include 
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 
#include 
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 
#include 
#include 
#include 
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 
#include  //   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
}

授业は全部终わりました!!!!!!!!!!!