Topic 1: 함수 템플릿과 클래스 템플릿 🔧
🎯 학습 목표
- 템플릿이 필요한 이유와 장점을 이해할 수 있다
- 함수 템플릿을 선언하고 사용하는 방법을 익힐 수 있다
- 클래스 템플릿을 만들고 활용하는 방법을 배울 수 있다
- template 키워드와 타입 매개변수를 정확히 사용할 수 있다
- STL 컨테이너의 동작 원리를 이해할 수 있다
🧰 만능 도구상자의 비밀
여러분, 만능 도구상자를 상상해보세요! 🧰
보통 도구상자에는 다양한 크기의 렌치가 들어있죠:
- 10mm 렌치 🔧
- 12mm 렌치 🔧
- 14mm 렌치 🔧
- 16mm 렌치 🔧
하지만 만능 렌치가 있다면? 크기를 조절해서 모든 볼트에 사용할 수 있어요!
C++ 템플릿도 바로 이런 “만능 도구” 같은 존재입니다. 하나의 코드로 여러 타입에 대응할 수 있죠!
🤔 기존 방식의 문제점
타입별로 함수를 따로 만들어야 하는 불편함
#include <iostream>
using namespace std;
// 정수용 교환 함수
void swap_int(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
// 실수용 교환 함수
void swap_double(double& a, double& b) {
double temp = a;
a = b;
b = temp;
}
// 문자열용 교환 함수
void swap_string(string& a, string& b) {
string temp = a;
a = b;
b = temp;
}
int main() {
// 😰 타입마다 다른 함수를 호출해야 함
int x = 10, y = 20;
swap_int(x, y);
double m = 3.14, n = 2.71;
swap_double(m, n);
// 새로운 타입이 나올 때마다 함수를 또 만들어야...
return 0;
}문제점들:
- 🔄 같은 로직을 타입마다 반복 작성
- 📈 코드량이 기하급수적으로 증가
- 🐛 버그 발생 시 모든 함수를 수정해야 함
- 😵 새로운 타입이 나올 때마다 함수를 추가해야 함
🔧 함수 템플릿의 마법
하나의 함수로 모든 타입 처리!
#include <iostream>
#include <string>
using namespace std;
// 🎩 마법의 템플릿 함수!
template <typename T> // T는 타입 매개변수
void magicSwap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
// 최대값을 찾는 템플릿 함수
template <typename T>
T findMax(T a, T b) {
return (a > b) ? a : b;
}
int main() {
cout << "🔧 함수 템플릿 실습" << endl;
// 정수 교환
int x = 100, y = 200;
cout << "교환 전: x=" << x << ", y=" << y << endl;
magicSwap(x, y); // 컴파일러가 자동으로 int 버전 생성!
cout << "교환 후: x=" << x << ", y=" << y << " 🔄" << endl;
// 실수 교환
double m = 3.14, n = 2.71;
cout << "\n교환 전: m=" << m << ", n=" << n << endl;
magicSwap(m, n); // 컴파일러가 자동으로 double 버전 생성!
cout << "교환 후: m=" << m << ", n=" << n << " 🔄" << endl;
// 문자열 교환
string hero = "용사", wizard = "마법사";
cout << "\n교환 전: " << hero << ", " << wizard << endl;
magicSwap(hero, wizard); // 컴파일러가 자동으로 string 버전 생성!
cout << "교환 후: " << hero << ", " << wizard << " 🔄" << endl;
// 최대값 찾기
cout << "\n최대값 비교:" << endl;
cout << "max(10, 20) = " << findMax(10, 20) << " 📊" << endl;
cout << "max(3.14, 2.71) = " << findMax(3.14, 2.71) << " 📊" << endl;
cout << "max(\"apple\", \"banana\") = " << findMax(string("apple"), string("banana")) << " 📊" << endl;
return 0;
}실행 결과:
🔧 함수 템플릿 실습
교환 전: x=100, y=200
교환 후: x=200, y=100 🔄
교환 전: m=3.14, n=2.71
교환 후: m=2.71, n=3.14 🔄
교환 전: 용사, 마법사
교환 후: 마법사, 용사 🔄
최대값 비교:
max(10, 20) = 20 📊
max(3.14, 2.71) = 3.14 📊
max("apple", "banana") = banana 📊템플릿 문법 상세 분석
template <typename T> // 템플릿 선언
// ^^^^^^^^ ^^
// 키워드 타입 매개변수
void functionName(T parameter) {
// T를 실제 타입처럼 사용
}
// 다른 방식으로도 쓸 수 있어요
template <class T> // typename과 class는 동일한 의미
void anotherFunction(T param) { }
// 여러 타입 매개변수도 가능!
template <typename T, typename U>
void multiTypeFunction(T first, U second) {
// T와 U는 서로 다른 타입일 수 있음
}📦 클래스 템플릿 만들기
붕어빵 틀의 진화: 만능 붕어빵 틀!
일반 붕어빵 틀은 팥만 넣을 수 있지만, 템플릿 붕어빵 틀은 팥, 슈크림, 피자, 심지어 아이스크림까지! 🍥
게임 인벤토리 클래스 템플릿
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// 🎮 범용 게임 인벤토리 템플릿
template <typename ItemType>
class GameInventory {
private:
vector<ItemType> items; // 아이템 보관함
int maxCapacity; // 최대 용량
public:
// 생성자
GameInventory(int capacity = 10) : maxCapacity(capacity) {
cout << "🎒 " << capacity << "칸 인벤토리가 생성되었습니다!" << endl;
}
// 아이템 추가
bool addItem(const ItemType& item) {
if (items.size() < maxCapacity) {
items.push_back(item);
cout << "✅ 아이템 추가: " << item << endl;
return true;
} else {
cout << "❌ 인벤토리가 가득 참! 용량: " << maxCapacity << endl;
return false;
}
}
// 아이템 제거
bool removeItem(const ItemType& item) {
for (auto it = items.begin(); it != items.end(); ++it) {
if (*it == item) {
items.erase(it);
cout << "🗑️ 아이템 제거: " << item << endl;
return true;
}
}
cout << "❌ 아이템을 찾을 수 없음: " << item << endl;
return false;
}
// 인벤토리 보기
void showInventory() const {
cout << "\n🎒 === 인벤토리 상태 ===" << endl;
cout << "용량: " << items.size() << "/" << maxCapacity << endl;
if (items.empty()) {
cout << "📭 인벤토리가 비어있습니다." << endl;
} else {
cout << "📦 보유 아이템:" << endl;
for (int i = 0; i < items.size(); ++i) {
cout << "[" << (i + 1) << "] " << items[i] << endl;
}
}
cout << "========================\n" << endl;
}
// 아이템 개수 반환
int getItemCount() const {
return items.size();
}
// 특정 아이템 검색
bool hasItem(const ItemType& item) const {
for (const auto& i : items) {
if (i == item) return true;
}
return false;
}
};클래스 템플릿 사용 예제
int main() {
cout << "🎮 === 게임 인벤토리 시스템 ===" << endl;
// 문자열 타입 인벤토리 (무기 보관함)
GameInventory<string> weaponBag(5);
weaponBag.addItem("⚔️ 전설의 검");
weaponBag.addItem("🏹 엘프의 활");
weaponBag.addItem("🛡️ 용의 방패");
weaponBag.showInventory();
// 정수 타입 인벤토리 (포션 개수 관리)
GameInventory<int> potionBag(3);
potionBag.addItem(5); // HP 포션 5개
potionBag.addItem(3); // MP 포션 3개
potionBag.addItem(1); // 부활 포션 1개
potionBag.showInventory();
// 실수 타입 인벤토리 (골드 관리)
GameInventory<double> goldPouch(10);
goldPouch.addItem(1250.50); // 1250.5 골드
goldPouch.addItem(3780.25); // 3780.25 골드
goldPouch.showInventory();
// 아이템 검색 및 제거
cout << "🔍 아이템 검색 테스트:" << endl;
cout << "전설의 검 보유? " << (weaponBag.hasItem("⚔️ 전설의 검") ? "✅" : "❌") << endl;
cout << "마법의 지팡이 보유? " << (weaponBag.hasItem("🪄 마법의 지팡이") ? "✅" : "❌") << endl;
weaponBag.removeItem("🏹 엘프의 활");
weaponBag.showInventory();
return 0;
}실행 결과:
🎮 === 게임 인벤토리 시스템 ===
🎒 5칸 인벤토리가 생성되었습니다!
✅ 아이템 추가: ⚔️ 전설의 검
✅ 아이템 추가: 🏹 엘프의 활
✅ 아이템 추가: 🛡️ 용의 방패
🎒 === 인벤토리 상태 ===
용량: 3/5
📦 보유 아이템:
[1] ⚔️ 전설의 검
[2] 🏹 엘프의 활
[3] 🛡️ 용의 방패
========================
🎒 3칸 인벤토리가 생성되었습니다!
✅ 아이템 추가: 5
✅ 아이템 추가: 3
✅ 아이템 추가: 1
🎒 === 인벤토리 상태 ===
용량: 3/3
📦 보유 아이템:
[1] 5
[2] 3
[3] 1
========================
🎒 10칸 인벤토리가 생성되었습니다!
✅ 아이템 추가: 1250.5
✅ 아이템 추가: 3780.25
🎒 === 인벤토리 상태 ===
용량: 2/10
📦 보유 아이템:
[1] 1250.5
[2] 3780.25
========================
🔍 아이템 검색 테스트:
전설의 검 보유? ✅
마법의 지팡이 보유? ❌
🗑️ 아이템 제거: 🏹 엘프의 활
🎒 === 인벤토리 상태 ===
용량: 2/5
📦 보유 아이템:
[1] ⚔️ 전설의 검
[2] 🛡️ 용의 방패
========================🎮 실전 예제: 범용 스택 컨테이너
STL의 stack과 유사한 컨테이너를 만들어봅시다!
#include <iostream>
#include <string>
#include <stdexcept>
using namespace std;
// 🥞 범용 스택 템플릿
template <typename T>
class GameStack {
private:
static const int MAX_SIZE = 100; // 최대 크기
T data[MAX_SIZE]; // 데이터 저장 배열
int topIndex; // 스택 상단 인덱스
public:
// 생성자
GameStack() : topIndex(-1) {
cout << "🥞 스택이 생성되었습니다! (최대 " << MAX_SIZE << "개)" << endl;
}
// 스택에 추가 (push)
void push(const T& item) {
if (topIndex >= MAX_SIZE - 1) {
throw overflow_error("🚨 스택 오버플로우! 더 이상 추가할 수 없습니다.");
}
data[++topIndex] = item;
cout << "📥 PUSH: " << item << " (크기: " << size() << ")" << endl;
}
// 스택에서 제거 (pop)
void pop() {
if (isEmpty()) {
throw underflow_error("🚨 스택 언더플로우! 제거할 요소가 없습니다.");
}
cout << "📤 POP: " << data[topIndex] << " (크기: " << (size() - 1) << ")" << endl;
topIndex--;
}
// 맨 위 요소 확인 (top)
T top() const {
if (isEmpty()) {
throw underflow_error("🚨 스택이 비어있습니다!");
}
return data[topIndex];
}
// 스택이 비어있는지 확인
bool isEmpty() const {
return topIndex == -1;
}
// 스택 크기 반환
int size() const {
return topIndex + 1;
}
// 스택 전체 출력
void display() const {
cout << "\n🥞 === 스택 상태 ===" << endl;
if (isEmpty()) {
cout << "📭 스택이 비어있습니다." << endl;
} else {
cout << "크기: " << size() << "/" << MAX_SIZE << endl;
cout << "스택 내용 (위→아래):" << endl;
for (int i = topIndex; i >= 0; i--) {
cout << "[" << i << "] " << data[i];
if (i == topIndex) cout << " ← TOP";
cout << endl;
}
}
cout << "==================\n" << endl;
}
};
int main() {
cout << "🎮 === 범용 스택 컨테이너 테스트 ===" << endl;
try {
// 문자열 스택
GameStack<string> commandStack;
commandStack.push("⚔️ 공격");
commandStack.push("🛡️ 방어");
commandStack.push("🏃 도망");
commandStack.display();
cout << "현재 명령: " << commandStack.top() << " 🎯" << endl;
commandStack.pop();
commandStack.pop();
commandStack.display();
// 정수 스택
cout << "\n--- 정수 스택 테스트 ---" << endl;
GameStack<int> scoreStack;
scoreStack.push(100);
scoreStack.push(250);
scoreStack.push(500);
scoreStack.display();
cout << "최고 점수: " << scoreStack.top() << "점 🏆" << endl;
// 실수 스택
cout << "\n--- 실수 스택 테스트 ---" << endl;
GameStack<double> healthStack;
healthStack.push(100.0);
healthStack.push(75.5);
healthStack.push(50.2);
healthStack.display();
} catch (const exception& e) {
cout << "⚠️ 예외 발생: " << e.what() << endl;
}
return 0;
}💡 템플릿의 핵심 개념
1. 🎭 컴파일 타임 다형성
템플릿은 컴파일 시점에 실제 타입으로 치환됩니다.
2. 🔄 코드 재사용성
하나의 코드로 여러 타입을 처리할 수 있어 중복을 줄입니다.
3. ⚡ 성능 최적화
런타임 오버헤드가 없이 타입별로 최적화된 코드가 생성됩니다.
4. 🔧 STL의 기반
vector, list, map 등 STL 컨테이너들이 모두 템플릿으로 구현되어 있습니다.
📚 STL 컨테이너와의 연결
우리가 사용하는 STL 컨테이너들도 모두 템플릿입니다!
vector<int> numbers; // vector 템플릿의 int 특수화
vector<string> names; // vector 템플릿의 string 특수화
map<string, int> scores; // map 템플릿의 <string, int> 특수화이제 여러분도 STL이 어떻게 작동하는지 이해하셨죠? 🎯
💡 핵심 정리
- 템플릿: 타입에 독립적인 범용 코드를 작성하는 기법
template <typename T>: 템플릿 선언 키워드- 타입 매개변수: T, U 등 실제 타입을 대신하는 플레이스홀더
- 함수 템플릿: 여러 타입에 대해 동일한 로직을 수행하는 함수
- 클래스 템플릿: 여러 타입에 대해 동일한 구조를 가지는 클래스
- 특수화: 템플릿이 특정 타입에 대해 구체적인 구현으로 변환되는 과정
✅ 실습 체크리스트
🚀 다음 시간 예고
다음 시간에는 STL 컨테이너 마스터하기에 대해 배워볼 거예요!
- vector, list, deque의 특징과 사용법
- map, set으로 고급 데이터 구조 활용
- 컨테이너 성능 비교와 최적 선택
- 실제 프로젝트에서 활용하는 STL 패턴들
“템플릿은 C++의 강력한 무기입니다! 🎯”
Last updated on