Skip to Content
💻 코리아IT아카데미 신촌 - 프로그래밍 학습 자료
C++ 프로그래밍Unit 13: 템플릿과 STLTopic 1: 함수 템플릿과 클래스 템플릿

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