2021 시작

예외 안전성을 확보하는 작업은 매우 힘들다. 다음 예쁜 배경 그림을 깔고 나오는 GUI 메뉴를 구현하기 위해 클래스를 하나 만든다고 가정하겠다(병행성 제어를 위해 Mutex사용)

class Image
{
public:
	Image(const istream& imgSrc) {}
};

class PrettyMenu
{
private:
	Mutex mutex;		//이 객체 하나를 위한 뮤텍스
	Image* bgImage;		//현재의 배경 그림
	int imageChanges;	//배경 그림이 바뀐 횟수


public:
	...;
	void ChangeBackground(const istream& imgSrc);	//배경 그림을
						//바꾸는 멤버 함수

	...;

};

void PrettyMenu::ChangeBackground(const istream& imgSrc)
{
	lock(&mutex);						//항목 14의 mutex다

	delete bgImage;						//이전 배경그림을 없앤다.
	++imageChanges;						//그림 변경 횟수를 갱신한다.
	bgImage = new Image(imgSrc);		//새 배경그림을 깔아 놓는다.

	unlock(&mutex);						//뮤텍스를 해제

}

 

위의 코드를 보면 예외 안전성이라곤 하나도 볼 수 없는 코드이다. 예외 안정성을 가진 함수라면 예외 때 다음과 같이 동작하여야 한다.

1. 자원이 새도록 만들면 안된다.

만약에 new image(imgSrc) 표현식에서 예외가 발생하면 unlock함수가 실행되지 않아 뮤텍스가 계속 잡혀있게 된다.

 

2. 자료구조가 더럽혀지는 것을 허용하지 않는다.

만약에 new Image(imgSrc)에서 예외가 던져지면 bgImage가 가리키는 객체는 이미 삭제되어 있을 뿐만 아니라 그림 변경이 되지 않았음에도 변경 카운터가 증가된다.

 

그럼 코드를 다음과 같이 바꿔보자.

struct unlock 
{
	template<typename T>
	void operator()(T* p) 
	{
		//삭제자 루틴
	}
};



class Image 
{
public:
	Image(const istream& imgSrc) {}
};

class PrettyMenu 
{
private:
	Mutex mutex;		//이 객체 하나를 위한 뮤텍스
	Image* bgImage;		//현재의 배경 그림
	int imageChanges;	//배경 그림이 바뀐 횟수

	
public:
	...;
	void ChangeBackground(const istream& imgSrc);	//배경 그림을
						//바꾸는 멤버 함수

	...;

};

class Lock 
{
private:
	shared_ptr<Mutex> mutexPtr;

public:
	explicit Lock(Mutex* pm)
		: mutexPtr(pm, unlock()) 
	{

		lock(mutexPtr);

	}

};

void PrettyMenu::ChangeBackground(const istream& imgSrc) 
{
	Lock m(&mutex);		//항목 14의 mutex다
	
	delete bgImage;		//이전 배경그림을 없앱니다.
	++imageChanges;		//그림 변경 횟수를 갱신한다.
	bgImage = new Image(imgSrc);

}

 

Lock과 같은 자원관리 클래스가 있으면 함수의 코드 길이가 짧아지게 되어 실수도 줄게 된다.

 

다음 내용을 이해하기 위해 3가지 용어를 알아야 한다.

1. 기본적인 보장

함수 동작중 예외가 발생하면 실행 중인 프로그램에 관련된 모든 것들을 유효한 상태로 유지하겠다는 보장이다. 이렇게 되면 함수를 만든 사람만이 어떻게 돌아갈지 예상할 수 있지만 사용자는 지금 사진이 무슨 사진인지 확인하는 함수를 안 쓰는 이상 모른다.(사용자는 예상 불가)

 

2. 강력한 보장

함수 동작 중에 예외가 발생하면, 프로그램의 상태를 절대로 변경되지 않겠다는 보장이다. A라는 함수가 진행 도중 예외로 인해 중간에 실패해도 값들이 함수 실행 전과 똑같다는 걸 보장한다고 생각하면 된다.

 

3. 예외 불가 보장

이건 모르겠어 ㅎㅎ;; 나중에 따로 알아볼게

 

다시 ChangeBackground함수로 돌아오겠다. 2번째  소스에서도 못 고친 것이 있다. 배경 화면 카운팅 변수에 대해서다. 그것은 문장의 재배치만으로 해결이 가능하다.(shared_ptr사용함)

class PrettyMenu 
{
private:
	Mutex mutex;		//이 객체 하나를 위한 뮤텍스
	shared_ptr<Image> bgImage;		//현재의 배경 그림
	int imageChanges;	//배경 그림이 바뀐 횟수
	
	
public:
	...;
	void ChangeBackground(const istream& imgSrc);	//배경 그림을
						//바꾸는 멤버 함수

	...;

};

void PrettyMenu::ChangeBackground(const istream& imgSrc) 
{
	Lock m(&mutex);		//항목 14의 mutex다
	
	bgImage.reset(new Image(imgSrc));	//이전 배경그림을 없애고
    								//새로운걸로 교체!
	++imageChanges;			//그림 변경 횟수를 갱신한다.
}

참고로 shared_ptr::reset함수는 매개변수("new Image(imgSrc)"의 결과가 제대로 생성되어야 호출이 가능하며 이 함수 안에 delete가 있으니 기존 것을 삭제하고 매개변수에 있는 새로운 값을 다시 할당한다. 하지만 이것만으로는 강력한 보장이 될 수 없다. 강력한 보장으로 바꾸기 위해서는 복사 후-맞바꾸기 기법을 쓰면 된다.

 

struct PMImp1 			//PMImp1 = "PrettyMenu
{	
	shared_ptr<Image>bgImage;	//Imp1."; PMImp1이 struct로 
	int imageChanges;			//선언된 데에는 이유가 있다.
};						//밑에서 확인하자

class PrettyMenu 
{
private:
	Mutex mutex;		//이 객체 하나를 위한 뮤텍스
	Image* bgImage;		//현재의 배경 그림
	int imageChanges;	//배경 그림이 바뀐 횟수
	shared_ptr<PMImp1>pImp1;

	
public:
	...;
	void ChangeBackground(const istream& imgSrc);	//배경 그림을
						//바꾸는 멤버 함수
	...;

};



void PrettyMenu::ChangeBackground(const istream& imgSrc) 
{
	Lock m(&mutex);		//항목 14의 mutex다
	shared_ptr<PMImp1>pNew(new PMImp1(*pImp1));		//객체의 데이터 부분을 복사한다.

	pNew->bgImage.reset(new Image(imgSrc));			//사본을 수정한다.
	++pNew->imageChanges;

	swap(pImp1, pNew);		//새 데이터로 바꿔 넣기
					//진짜로 배경 그림을 바꾼다.
					
}					//뮤텍스를 놓는다.

 

강력한 보장은 그만큼 연산이 많으므로 중요한 데만 써보자

언제나 "어떻게 하면 예외처리를 잘할까?"라고 생각하는 버릇을 들이자

예외에 안전하거나 예외가 뚫려있거나 이거 두 가지뿐이다.

 

이것만은 잊지 말자!

1. 예외 안전성을 갖춘 함수는 실행 중 예외가 발생되더라도 자원을 누출시키지 않으며 자료구조를 더럽힌 채로 내버려 두지 않는다. 이런 함수들이 제공할 수 있는 예외 안정성 보장은 기본적인 보장, 강력한 보장, 예외 금지 보장이 있다.

 

2. 강력한 예외 안전성 보장은 '복사-후-맞바꾸기' 방법을 써서 구현할 수 있지만, 모든 함수에 대해 강력한 보장이 실용적인 것은 아니다.

 

3. 어떤 함수가 제공하는 예외 안전성 보장의 강도는 , 그 함수가 내부적으로 호출하는 함수들이 제공하는 가장 약한 보장을 넘지 않는다.

 

 

공유하기

facebook twitter kakaoTalk kakaostory naver band
loading