레슨 9 / 10·4개 토픽
템플릿과 예외 처리
함수 템플릿
템플릿은 타입에 독립적인 코드를 작성할 수 있게 해주는 C++의 제네릭 프로그래밍 도구입니다. 컴파일 시점에 사용된 타입에 맞는 코드가 자동으로 생성됩니다.
cpp
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// 함수 템플릿
template<typename T>
T maxValue(T a, T b) {
return (a > b) ? a : b;
}
// 여러 타입 매개변수
template<typename T, typename U>
auto add(T a, U b) -> decltype(a + b) {
return a + b;
}
// 템플릿 특수화
template<>
const char* maxValue<const char*>(const char* a, const char* b) {
return (strcmp(a, b) > 0) ? a : b;
}
int main() {
cout << maxValue(3, 7) << endl; // 7 (int)
cout << maxValue(3.14, 2.71) << endl; // 3.14 (double)
cout << maxValue("abc", "xyz") << endl; // xyz (특수화)
cout << add(3, 4.5) << endl; // 7.5 (int + double)
return 0;
}클래스 템플릿
cpp
#include <iostream>
#include <stdexcept>
using namespace std;
// 클래스 템플릿 — 제네릭 스택
template<typename T, int MaxSize = 100>
class Stack {
T data[MaxSize];
int top;
public:
Stack() : top(-1) {}
void push(const T& item) {
if (top >= MaxSize - 1)
throw overflow_error("스택 오버플로");
data[++top] = item;
}
T pop() {
if (top < 0)
throw underflow_error("스택 언더플로");
return data[top--];
}
bool empty() const { return top < 0; }
int size() const { return top + 1; }
};
int main() {
Stack<int, 50> intStack; // int 스택, 최대 50개
Stack<string> strStack; // string 스택, 기본 100개
intStack.push(10);
intStack.push(20);
cout << intStack.pop() << endl; // 20
strStack.push("Hello");
strStack.push("World");
cout << strStack.pop() << endl; // World
return 0;
}예외 처리
C++의 예외 처리는 try/catch/throw로 구성됩니다. std::exception을 상속하여 커스텀 예외를 만들 수 있으며, 소멸자에서 리소스를 해제하는 RAII 패턴과 함께 사용하면 예외 안전한 코드를 작성할 수 있습니다.
cpp
#include <iostream>
#include <stdexcept>
#include <string>
#include <fstream>
using namespace std;
// 커스텀 예외
class ValidationError : public runtime_error {
int errorCode;
public:
ValidationError(const string& msg, int code)
: runtime_error(msg), errorCode(code) {}
int getCode() const { return errorCode; }
};
int divide(int a, int b) {
if (b == 0)
throw invalid_argument("0으로 나눌 수 없습니다");
return a / b;
}
void validateAge(int age) {
if (age < 0 || age > 150)
throw ValidationError("잘못된 나이: " + to_string(age), 1001);
}
int main() {
// 기본 try/catch
try {
cout << divide(10, 0) << endl;
} catch (const invalid_argument& e) {
cerr << "에러: " << e.what() << endl;
}
// 커스텀 예외
try {
validateAge(200);
} catch (const ValidationError& e) {
cerr << "검증 실패 [" << e.getCode() << "]: "
<< e.what() << endl;
}
// 여러 catch 블록
try {
throw runtime_error("런타임 에러 발생");
} catch (const runtime_error& e) {
cerr << "런타임: " << e.what() << endl;
} catch (const exception& e) {
cerr << "일반: " << e.what() << endl;
} catch (...) {
cerr << "알 수 없는 예외" << endl;
}
return 0;
}RAII 패턴
cpp
#include <iostream>
#include <fstream>
#include <mutex>
using namespace std;
// RAII — Resource Acquisition Is Initialization
// 리소스 획득을 객체 초기화에, 해제를 소멸자에 맡김
// RAII 파일 래퍼
class FileHandler {
ofstream file;
public:
FileHandler(const string& filename) : file(filename) {
if (!file.is_open())
throw runtime_error("파일 열기 실패: " + filename);
cout << "파일 열림: " << filename << endl;
}
~FileHandler() {
if (file.is_open()) {
file.close();
cout << "파일 닫힘" << endl;
}
}
void write(const string& text) {
file << text << endl;
}
};
// RAII 뮤텍스 잠금 (std::lock_guard와 동일 원리)
class LockGuard {
mutex& mtx;
public:
LockGuard(mutex& m) : mtx(m) { mtx.lock(); }
~LockGuard() { mtx.unlock(); }
};
int main() {
// 예외가 발생해도 소멸자가 호출되어 파일이 닫힘
try {
FileHandler fh("output.txt");
fh.write("Hello, RAII!");
// 블록 종료 시 ~FileHandler() 자동 호출
} catch (const exception& e) {
cerr << e.what() << endl;
}
// 표준 라이브러리의 RAII 도구들
// unique_ptr — 동적 메모리 자동 해제
// lock_guard — 뮤텍스 자동 해제
// fstream — 파일 자동 닫기
return 0;
}- •
template— 타입 매개변수를 선언하여 제네릭 코드 작성 - •템플릿 특수화 — 특정 타입에 대해 별도의 구현 제공
- •
try/catch/throw— 예외를 발생시키고 처리하는 메커니즘 - •
std::exception— 모든 표준 예외의 기반 클래스 - •
what()— 예외 메시지를 반환하는 가상 함수 - •RAII — 생성자에서 리소스 획득, 소멸자에서 리소스 해제
- •
catch(...)— 모든 타입의 예외를 잡는 범용 핸들러
💡
RAII는 C++에서 가장 중요한 관용구 중 하나입니다. 직접 new/delete를 사용하는 대신 unique_ptr, shared_ptr 같은 스마트 포인터를 사용하고, 뮤텍스는 lock_guard로 관리하세요. 예외가 발생해도 리소스 누수를 방지할 수 있습니다.