[大杯C++14例外処理

32233 ワード

Created: June 6, 2021 11:26 AM
Tag: exception handling, function try, rethrow, stack unwinding, std::exception

14.1異常処理のデフォルト異常処理


異常処理は、trycatch(投げ出された異常を受け付けて処理)、throw(投げ出された異常)文で行うことができる.この方法は文法的には簡潔だが、演技には不利なので、場合によっては使用を制限しなければならない.また,ランタイムエラー(runtime error)はデータ型に応じて厳密に動作するため注意が必要である.ellipses文でこれを防ぐことができます.
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

int main(void)
{
	double x;
	cin >> x;

	try
	{
		if (x < 0.0) throw std::string("Negative input");

		cout << std::sqrt(x) << endl;
	}
	catch (std::string error_message) // <- 자료형에 주의
	{
		// do something to respond
		cout << error_message << endl;
	}

	return (0);
}

14.2異常処理とスタック後退スタック展開

#include <iostream>
using namespace std;

void last() throw(int) // throw(int) <- exception specifier: int type의 예외를 던질 수도 있음을 명시!
// 불필요한것 아니냐는 견해
// 예외를 던질 가능성이 있는 함수
{
	cout << "last " << endl;
	cout << "throws exception" << endl;

	throw -1; // 다음부분 실행하지 않고 throw

	cout << "end last " << endl;
}

void third()
{
	cout << "third" << endl;

	last(); // 다음부분 실행하지 않고 throw

	cout << "end third" << endl;
}

void second()
{
	cout << "second" << endl;

	try
	{
		third();
	}
	catch (double) // throw는 int로 되었는데 double로 밖에 catch하지 못한다면 계속해서 날아간다
	{
		cerr << "second  caught double exception" << endl;
	}

	cout << "end second " << endl;
}

void first()
{
	cout << "first" << endl;

	try
	{
		second();
	}
	catch (int) // catch!
	{
		cerr << "first caught int exception" << endl;
	}

	cout << "end first" << endl;
}

int main(void)
{
	cout << "start" << endl;

	try
	{
		first();
	}
	catch (int)
	{
		cerr << "main caught int exception" << endl;
		// clog
	}

	// uncaught exceptions 못잡는 오류가 발생하는 경우
	catch (...) // catch-all handlers
	{
		cerr << "main caught ellipses exception" << endl;
	}

	cout << "end main" << endl;

	return (0);
}

14.3例外クラス(カスタムデータ型)と継承時の注意点


例外クラスが継承されると、catch文でオブジェクトのクリップのような現象が発生する可能性があります.このような状況を防ぐ方法とrethrowについて説明しましょう.
#include <iostream>
using namespace std;

class Exception
{
public:
	void report()
	{
		cerr << "Exception report" << endl;
	}
};

class ArrayException : public Exception
{
public:
	void report()
	{
		cerr << "Array exception" << endl;
	}
};

class MyArray
{
private:
	int m_data[5];

public:
	int &operator [] (const int &index)
	{
		if (index < 0 || index >= 5) throw ArrayException();

		return (m_data[index]);
	}
};

void doSomething()
{
	MyArray my_array;

	try
	{
		my_array[100];
	}
	catch (const int &x)
	{
		cerr << "exception" << x << endl;
	}
	catch (ArrayException &e)
	{
		e.report();
		throw e; // rethrow
		// throw; // 객체 잘림 발생하지 않음
	}
	catch (Exception &e)
	// catch (Exception &e)이 먼저 작성되어 있다면
	// 받는 쪽에서 ArrayException이 아닌 Exception으로 받기 때문에
	// 객체잘림과 유사하게 Exception report 출력
	{
		e.report();
	}
}

int main(void)
{
	try
	{
		doSomething();
	}
	catch(ArrayException &e)
	{
		cout << "main()" << endl; // rethrow 한것을 받는다
		e.report();
	}

	return (0);
}

14.4 std::例外の概要


異なる状況に対する異常処理はstd::exceptionによって実現された.それをどのように利用するかを理解してみましょう.
#include <iostream>
#include <exception> // <-
#include <string>

class CustomException : public std::exception
{
public:
	const char *what() const noexcept override
	{
		return ("Custom exception");
	}
};

int main(void)
{
	try
	{
		throw std::runtime_error("Bad thing happend");
	}
	catch (std::length_error &exception)
	{
		std::cerr << exception.what() << std::endl;
	}
	catch (std::exception &exception)
	{
		std::cerr << exception.what() << std::endl;
	}

	return (0);
}

14.5関数try

#include <iostream>
using namespace std;

class A
{
private:
	int m_x;
public:
	A(int x) : m_x(x)
	{
		if (x <= 0)
			throw 1;
	}
	B(int x) try : A(x)
	{
		// do init
	}
	catch (...)
	{
		cout << "catch in B constructor " << endl;
		// throw; // 없어도 있는것처럼 작동!
	}
};

class B : public A
{
public:
	B (int x)
		: A(x) // init
	{
	}
};

void doSomething()
{
	try
	{
		throw - 1;
	}
	catch (...)
	{
		cout << "catch in doSomething()" << endl;
	}
}

int main(void)
{
	try
	{
		// doSomething()
		B b(0);
	}
	catch (...)
	{
		cout << "catch in main(void)" << endl;
	}

	return (0);
}

14.6異常処理のリスクと欠点

  • 消滅者のうち、throwはタブーとされている.→ runtime error!
  • スマートポインタを使用すると、領域を離れると(throw)メモリが自動的に解放されます.
  • #include <iostream>
    #include <memory>
    using namespace std;
    
    class A
    {
    public:
    	~A()
    	{
    		throw "error"; // <-
    	}
    };
    
    int main(void)
    {
    	try
    	{
    		int *i = new int[10000000];
    		unique_ptr<int> up_i(i); // <-
    
    		// do something with i
    		throw "error";
    
    		// delete[] i;
    
    		// A a;
    	}
    	catch (...)
    	{
    		cout << "catch" << endl;
    	}
    
    	return (0);
    }