LastMod:

👨‍💻 개인 공부 기록용 블로그 입니다.
💡 틀린 내용이나 오타는 댓글, 메일로 제보해주시면 감사하겠습니다!! (__)

42 Cursus - Cpp Module을 진행하기 위해 정리했었던 C++98 기본 개념들로, C++11, C++14와 다른 내용이 있을 수 있습니다.
각 개념은 정리한 시간 순으로 배치되어 있으며, 한 포스트에 배치된 개념이 크게 관련이 없을 수 있습니다.

에외 처리 (Exception) 와 try-catch, throw

  • C에서는 오류는 거의 메모리 문제로 귀결 되었다. (seg fault, heap buffer overflow 등)
  • 그 외 기타 사용자 오류는 모두 if-else 조건문 분기를 통해 리턴값을 다르게 주는 형태로 처리하였다.
  • C++에서는 std::exception를 상속받은 클래스들을 throw 함으로써 각종 예외를 처리할 수 있다.
  • try 블록 안에서 throwexceptioncatch 구문을 통해 받을 수 있으며, 받은 exception의 종류에 따라 다양한 처리를 해줄 수 있다.

throw expression

throw "expression"
  • expression 자리는 예외 객체가 들어가며, throw 표현식에 의해 생성되는 임시 객체이다.
  • throw 표현식은 가장 가까운 catch로 흐름을 점프시킨다. (throw 밑에 있는 문장을 실행시키지 않는다.)
  • 따라서 소멸자만 제대로 작성하였다면 예외가 발생하여도 사용하고 있는 자원들을 제대로 소멸시킬 수 있다.

try-catch block

try { /* */ } catch (const std::exception& e) { /* */ }
  • 예외가 발생할 수 있는 코드를 try 블록 안에 넣는다.
  • 만약 try 블록 안에서 예외가 발생한다면 (throw 표현식이 작동한다면), 제일 가까운 catch가 해당 오류를 받아서 catch 블록 안에 있는 코드를 실행시킨다.
  • 예외가 발생하지 않는다면 catch 블록을 무시한다.
  • catch 구문의 매개변수가 레퍼런스인 이유는 해당 객체에 대한 모든 변경사항을 반영하여, exception이 또 다른 exception을 던질 때에도 다른 핸들러에서 관찰할 수 있게 하기 위함이다.
    • 만약 레퍼런스를 사용하지 않는다면, 모든 변경사항이 로컬에 저장 되어 해당 핸들러가 종료되자 마자 예외 객체가 사라진다.
    • 메인 try-catch의 try 블록에서 함수 호출 → 해당 함수 내에서도 try-catch 블록이 존재하며, try 블록 내에서 예외 발생 → catch 내에서 처리 후 다시 throw → 메인 try-catch에서도 해당 에러 객체를 받아서 처리 가능
try
{
    f();
}
catch (const std::overflow_error& e)
{} // this executes if f() throws std::overflow_error (same type rule)
catch (const std::runtime_error& e)
{} // this executes if f() throws std::underflow_error (base class rule)
catch (const std::exception& e)
{} // this executes if f() throws std::logic_error (base class rule)
catch (...)
{} // this executes if f() throws std::string or int or any other unrelated type
  • 위와 같이 여러 종류의 exception을 받을 수 있다.
  • try-catch 구문 내에서 switch 분기를 사용하거나 goto를 사용하면 안된다.

std::exception

  • what() 이라는 가상함수를 가진다.

      virtual const char* what() const throw(); // until C++11
      virtual const char* what() const noexcept; // since C++11
    
  • 함수 형태에서 볼 수 있다시피, 에러를 던지면서 출력해줄 문자열을 C스타일로 반환해야 한다.
  • 다양한 표준 에러 객체는 레퍼런스를 참고하자.

User Defined Exception

  • 프로그래머가 직접 예외를 정의할 때에는 위의 std::exception을 public 상속하여 클래스로 구현한다.
    • 반드시 public 상속해야 하며, 그렇지 않으면 런타임 에러가 발생한다. → terminating due to uncaught exception
// 구현하고자 하는 클래스 안에서
class MyException : public std::exception
{
	public:
		const char * what(void) const throw();
};
// 뒤에 붙는 throw()는 하나의 키워드로써, 예외가 발생하지 않는 멤버 변수라는 것을 컴파일러에게 알려주는 역할을한다.
// C++11부터는 throw()대신 noexcept 사용을 권장한다. (키워드로써 붙는 throw()는 deprecated)

스택 되감기 (Stack unwinding)

  • 일반적으로 함수를 호출할 때 Call Stack에 함수의 주소를 저장하고, 함수가 종료되었을 때 Call Stack에서 pop한다.
  • Stack unwinding은 try-catch문에서 함수 호출 중 예외가 발생하였을 때, Call Stack에 저장되었던 함수들의 주소가 제거되어 최초의 try-catch를 호출한 함수로 돌아가는 것을 말한다.

템플릿 (Template)

  • C++ 에서는 C에서와 달리, 동일한 함수 이름을 가지고 다양한 타입으로 재정의할 수 있다.
  • 하지만, 타입마다 재정의를 해줘야 하는 단점이 있는데, 이를 해결하기 위해 도입된 것이 템플릿 (Template)이다.
  • template <typename T> 와 같이 사용하고, 그 밑에 T에 대한 함수, 클래스를 정의하면 된다. 종류에 따라 클래스 템플릿, 함수 템플릿으로 나뉜다.
  • 템플릿은 인스턴스화 되기 전까지, 컴파일 타임에는 그냥 코드로 남아있다.

클래스 템플릿 (Class Templates)

  • 템플릿을 클래스에 대해 사용하는 것을 클래스 템플릿 (Class Templates) 이라고 한다.
  • 예를 들어, STL 컨테이너의 대표적인 한 종류인 벡터 (Vector)에 대해 생각할 수 있는데, 자료형에 따라 매번 벡터를 재정의할 수 없기 때문에 템플릿을 통해 구현되었다.
template <typename T>
class Vector {
  T* data;
  int capacity;
  // ...
  • 위에서 말했다시피, 컴파일 타임에는 그냥 코드로 남아있다가 해당 클래스가 인스턴스화 된다면, T가 인자로 전달된 타입으로 모두 치환된다.
  • 하지만 특정 T에 대해서 다른 처리를 해주고 싶을 수 있는데, 그 경우는 밑에 있는 템플릿 특수화에서 알아보자.

템플릿 특수화 (Templates Specialization)

  • 특정 타입에 대해 템플릿을 다르게 처리해주는 것을 템플릿 특수화 (Templates Specialization) 라고 한다.
  • 템플릿 특수화를 위해서는 아래와 같이 작성한다.
// 예를 들어, bool 타입의 vector를 따로 처리해주고 싶다면,
template <>
class Vector<bool> {
  // ...
// 와 같이 사용한다.
// 물론 ... 에는 T를 사용할 수 없다.

// 아래와 같이 여러 typename을 받는 클래스인 test에 대해서
template <typename A, typename B, typename C>
class test {};

// B 빼고 나머지 타입을 특정시키고 싶다면 아래와 같이 사용한다.
template <typename B>
class test<int, B, double> {};

함수 템플릿 (Function Templates)

  • 함수 템플릿 또한 위 특성을 모두 공유한다.
template <typename T>
T max(T& a, T& b) {
  return a > b ? a : b;
}

함수 객체 (Functor; Function Object)

  • 파이썬에서 정렬 시 key 함수를 따로 지정해주는 것 처럼, 객체 간 비교 등에 일반적이지 않은 다른 비교연산 사용하고 싶을 수 있다.
  • 물론 사용자 객체의 경우 연산자를 오버로딩 하여 구현하면 그만이지만, 문제는 기존에 구현된 객체 (int vs. string) 와 같은 경우는 그럴 수 없다는 것이다.
  • 아래와 같이 구현하여 사용할 수 있다.
struct Comp2 {
  bool operator()(int a, int b) { return a < b; }
};

Comp1 comp1;
std::sort(vec.begin(), vec.end(), comp1);

Reference

CPP 레퍼런스 (std::exception)

CPP 레퍼런스 (try-catch)

CPP 레퍼런스 (throw)

CPP 레퍼런스 (Exceptions)

씹어먹는 C++ - <9 - 1. 코드를 찍어내는 틀 - C++ 템플릿(template)>

CPP 레퍼런스 (템플릿)

CPP 레퍼런스 (템플릿 인자)

CPP 레퍼런스 (클래스 템플릿)

CPP 레퍼런스 (템플릿 특수화)

CPP 레퍼런스 (함수 템플릿)

CPP 레퍼런스 (함수 객체)

Leave a comment