[C++ 정리] 예외처리 2015-06-24

프로그램은 ‘버그(bug)’ / ‘오류(error)’ / ‘예외(exception)’에 의해서 멈추거나 종료되기도 한다. 세가지 단어를 구분하여 보자.

사전적인 의미

* bug : (컴퓨터 시스템이나 프로그램의)오류
* error : 오류
* exception : 예외

어느 저자의 정의

* bug : 프로그래머에 의한 프로그램 오작동 문제
  ※ NULL포인터 호출, 배열 오버플로우, 동적메모리 미삭제로 인한 메모리 누수 등
* error : 사용자에 의한 프로그램 오작동 문제
  ※ 할당된 크기보다 큰 문자열을 입력
* exception : 예상치 못한 error 발생에 의한 프로그램 오작동
  ※ 연결이 종료된 디비에 접근, 손상된 파일에 접근 등

결론

* 프로그램에 문제가 있거나 예외처리가 되지않았을때 '오류가 발생했다.', '예외처리가 안됬다.'라고 표현된다.
* 버그(bug)와 오류(error)를 궂이 나누자면 '프로그래머에 의한 버그', '사용자에 의한 오류'이다.
* 예외는 프로그램 외적인 요인에 의해 발생하는 것이다.

프로그램 개발시에는 예외에 의해 비정상 작동이 일어나지 않도록 예외처리를 해주어야 한다. 그래야만 견고한 프로그램이 되고 사용자는 이를 믿고 사용할 수 있게 된다. 예외처리에는 다음 2가지 유형이 있다.

1. 반환 값을 사용한 예외처리
#include "stdafx.h"
#include <iostream>

using namespace std;

class MyException
{
public:
	MyException(int number)
	{
		this->number = number;
	}

	int number;
	// >> 반환값을 사용한 예외처리
	// 더큰수만 입력받는 함수
	bool Test(int bigNumber){
		if (this->number >= bigNumber) return false;
		this->number = bigNumber;

		return true;
	}
};

int _tmain(int argc, _TCHAR* argv[])
{
	MyException myException(100);

	bool isTrue = myException.Test(50);
	if (!isTrue)
		cout << "Error : 터큰수만 입력가능합니다.\n";

	return 0;
}
장점
* 간편히 구현이 가능하다.
단점
* 호출시 마다 반환값을 받아 비교해 에러를 알려야한다.
* 프로그램이 커진다면 소스코드에 예외처리 코드가 엉켜 지저분하다.
* 반환 값이 결과를 알리는 목적이기 때문에 다른 용도로는 사용이 불가하다.
2. try catch를 활용한 예외처리
#include "stdafx.h"
#include <iostream>
#include <string>

using namespace std;

class MyException1
{
public:
	MyException1();
	MyException1(int code, string message){
		this->errorCode = code;
		this->errorMessage = message;
	}
	int errorCode;
	string errorMessage;
};

class MyException2
{
public:
	MyException2()
	{
		cout << "여러가지 Exception이 가능합니다.\n";
	}
};

class MyClass
{
public:
	MyClass(int number)
	{
		this->number = number;
	}

	int number;
	void Test(int bigNumber){
		if (this->number >= bigNumber) throw MyException1(100, "Error : 더큰수만 입력가능합니다.\n");
		if (bigNumber > 200) throw MyException2();
		this->number = bigNumber;
	}
};

int _tmain(int argc, _TCHAR* argv[])
{
	// 기본적인 예외처리
	try
	{
		MyClass myClass1(100);
		myClass1.Test(50);
	}
	catch (MyException1& ex)
	{
		cout << ex.errorMessage;
	}

	// 여러가지 예외상황을 고려할 수 있다.
	try
	{
		MyClass myClass2(100);
		myClass2.Test(201);
	}
	catch (MyException1& ex)
	{
		cout << ex.errorMessage;
	}
	catch (MyException2& ex)
	{
		cout << "Error : 너무 큰수를 입력하였습니다.\n";
	}

	return 0;
}
* 예외에 대하여 유연하게 처리할 수 있다.
* 여러예외를 잡아낼 수 있다.
* 예외 객체는 가능하면 참조로 받아 메모리 관리에 불편함이 없도록 하자.(MyException1& ex)
  ※ 객체로 받을 경우 불필요한 메모리 복사발생(MyException1 ex)
  ※ 포인터로 받을 경우 메모리 할당과 해제를 신경써줘야한다.(MyException1* ex)
* 여러 함수를 건너 사용할 수 있다.

[C++ 정리] 클래스의 접근 제어 2015-06-23

클래스 내부에 무조건 접근하지 못하도록 구현시 public / protected / private 의 3가지 키워드를 사용할 수 있다. 의미와 용도는 다음과 같다.

* public : 모든 곳으로부터 접근가능
* protected : 자식클래스의 멤버함수에서만 접근가능
* private : 자신의 멤버함수에서만 접근가능
구분 자신의 멤버함수 자식클래스의 멤버함수 모든곳
private 접근가능 접근불가 접근불가
protected 접근가능 접근가능 접근불가
public 접근가능 접근가능 접근가능

 

[C++ 정리] 상속의 기본형과 형변환 2015-06-23

부모 클래스에서 미리 구현된 내용을 상속받아 자식클래스에서 확장한다. 또한 부모 자식관계의 클래스에서 형변환은 기본적으로 ‘자식에 부모를 담지못한다’, ‘부모에 자식은 담을 수 있다’를 유념한다.

class MyClassParent
{
public:
	MyClassParent();
	MyClassParent(int x, int y)
	{
		this->width = x;
		this->height = y;
	}

protected:
	int width;
	int height;

private:
	// Error - private로 선언시 상속받은 클래스에서 접근할 수 없다.
	//  => protected 에 선언한다.
	//int width;
	//int height;
	
};

// 다음과 같은 형태로 상속 받는다.
class MyClass :public MyClassParent
{
public:
	MyClass();
	int getArea()
	{
		return this->width * this->height;
	}
	int initArea;
};

// 다음과 같은 형태로 부모클래스의 생성자를 호출할 수 있다.
MyClass::MyClass():MyClassParent(100, 20)
{
	this->initArea = this->width * this->height;
}

// - END header

int _tmain(int argc, _TCHAR* argv[])
{
	// 상속의 예
	MyClass initClass;
	cout << initClass.initArea;

	// 1>> 형변환	
	// [Error] 자식클래스에 부모객체는 담지 못한다.
	MyClassParent parentClass1;
	/*MyClass childClass1 = parentClass1;*/
	// [OK] 부모클래스에 자식객체는 담을 수 있다.
	MyClass childClass2;
	MyClassParent parentClass2 = childClass2;
	
	// 2>> 포인터간 형변환
	// [ERROR] 자식클래스의 포인터에 부모객체의 주소는 담지 못한다.
	MyClassParent parentClassPointer1;	
	/*MyClass* childClassPointer1 = &parentClassPointer1;*/
	// [OK] 부모클래스의 포인터에 자식객체의 주소는 담을 수 있다.
	MyClass childClassPointer2;	
	MyClassParent* parentClassPointer2 = &childClassPointer2;
	
	// 레퍼런스간 형변환
	// [ERROR] 자식클래스의 참조값에 부모객체의 주소는 담지 못한다.
	MyClassParent parentClassReference1;
	/*MyClass childClassReference1 = &parentClassReference1;*/
	// [OK] 부모클래스의 참조값에 자식객체의 주소는 담을 수 있다.
	MyClass childClassReference2;
	MyClassParent& parentClassReference1 = childClassReference2;

	return 0;
}
* 자식클래스에서는 부모클래스의 public: & protected: 에만 접근이 가능하다.
* 부모클래스에 자식객체는 담을 수 있다. 반대로는 불가하다.
* 부모클래스의 포인터에 자식객체의 주소는 담을 수 있다. 반대로는 불가하다.
* 부모클래스의 참조값에 자식객체의 주소는 담을 수 있다. 반대로는 불가하다.