Topic 3: 추상 클래스와 인터페이스 📐
🎯 학습 목표
- 순수 가상 함수의 개념을 이해할 수 있다
- 추상 클래스를 설계하고 활용할 수 있다
- 인터페이스 패턴을 구현할 수 있다
- 추상화의 중요성을 설명할 수 있다
📐 설계도로 이해하는 추상 클래스
추상 클래스는 마치 건축 설계도와 같습니다!
집 설계도의 특징 🏠
- 📝 설계도만으로는 살 수 없음: 실제 집을 지어야 함
- 🏗️ 기본 구조 제공: 방, 화장실, 주방의 위치
- 🎨 세부사항은 자유: 인테리어, 색상은 각자 선택
// 추상 클래스 = 설계도
class HouseBlueprint {
virtual void buildRoof() = 0; // 지붕 (구체적 방법은 미정)
virtual void buildWalls() = 0; // 벽 (구체적 방법은 미정)
virtual void buildDoor() = 0; // 문 (구체적 방법은 미정)
};
// 실제 집 = 구체 클래스
class MyHouse : public HouseBlueprint {
void buildRoof() { /* 기와 지붕 */ }
void buildWalls() { /* 벽돌 벽 */ }
void buildDoor() { /* 나무 문 */ }
};🌟 순수 가상 함수 (Pure Virtual Function)
순수 가상 함수란?
구현이 없는 가상 함수 = 자식 클래스가 반드시 구현해야 함!
class AbstractClass {
public:
// 순수 가상 함수 선언
virtual void pureVirtualFunction() = 0; // = 0이 핵심!
// 일반 가상 함수 (구현 있음)
virtual void normalVirtualFunction() {
cout << "기본 구현" << endl;
}
// 일반 함수
void normalFunction() {
cout << "일반 함수" << endl;
}
};🎮 실습: 게임 아이템 시스템
#include <iostream>
#include <vector>
#include <string>
using namespace std;
// 추상 클래스 - 게임 아이템
class Item {
protected:
string name;
int price;
string rarity; // 일반, 희귀, 전설
public:
Item(string n, int p, string r)
: name(n), price(p), rarity(r) {}
// 순수 가상 함수들 - 자식이 반드시 구현!
virtual void use() = 0;
virtual string getDescription() = 0;
virtual string getType() = 0;
// 일반 가상 함수 - 기본 구현 제공
virtual void showInfo() {
cout << "\n=== " << name << " ===" << endl;
cout << "종류: " << getType() << endl;
cout << "등급: " << rarity << endl;
cout << "가격: " << price << " 골드" << endl;
cout << "설명: " << getDescription() << endl;
}
// 일반 함수
string getName() const { return name; }
int getPrice() const { return price; }
};
// 무기 클래스
class Weapon : public Item {
private:
int damage;
public:
Weapon(string n, int p, string r, int dmg)
: Item(n, p, r), damage(dmg) {}
void use() override {
cout << "⚔️ " << name << "을(를) 장착했습니다!" << endl;
cout << " 공격력 +" << damage << endl;
}
string getDescription() override {
return "공격력을 " + to_string(damage) + " 증가시키는 무기";
}
string getType() override {
return "무기";
}
};
// 포션 클래스
class Potion : public Item {
private:
int healAmount;
public:
Potion(string n, int p, string r, int heal)
: Item(n, p, r), healAmount(heal) {}
void use() override {
cout << "🧪 " << name << "을(를) 마셨습니다!" << endl;
cout << " HP +" << healAmount << " 회복!" << endl;
}
string getDescription() override {
return "HP를 " + to_string(healAmount) + " 회복시키는 포션";
}
string getType() override {
return "포션";
}
};
// 방어구 클래스
class Armor : public Item {
private:
int defense;
public:
Armor(string n, int p, string r, int def)
: Item(n, p, r), defense(def) {}
void use() override {
cout << "🛡️ " << name << "을(를) 장착했습니다!" << endl;
cout << " 방어력 +" << defense << endl;
}
string getDescription() override {
return "방어력을 " + to_string(defense) + " 증가시키는 방어구";
}
string getType() override {
return "방어구";
}
};
// 인벤토리 시스템
class Inventory {
private:
vector<Item*> items;
int gold;
public:
Inventory(int initialGold = 1000) : gold(initialGold) {
cout << "🎒 인벤토리 생성 (보유 골드: " << gold << ")" << endl;
}
~Inventory() {
for (Item* item : items) {
delete item;
}
}
void addItem(Item* item) {
items.push_back(item);
cout << "✅ " << item->getName() << " 획득!" << endl;
}
void showAllItems() {
cout << "\n=== 🎒 인벤토리 ===" << endl;
cout << "보유 골드: " << gold << endl;
cout << "아이템 개수: " << items.size() << endl;
for (size_t i = 0; i < items.size(); i++) {
cout << "\n[" << i+1 << "]";
items[i]->showInfo();
}
}
void useItem(int index) {
if (index > 0 && index <= items.size()) {
items[index-1]->use();
} else {
cout << "❌ 잘못된 아이템 번호입니다!" << endl;
}
}
};
int main() {
cout << "=== RPG 아이템 시스템 ===" << endl;
Inventory myInventory(5000);
// 다양한 아이템 추가
myInventory.addItem(new Weapon("전설의 검", 1000, "전설", 50));
myInventory.addItem(new Potion("체력 포션", 50, "일반", 30));
myInventory.addItem(new Armor("드래곤 갑옷", 2000, "전설", 40));
myInventory.addItem(new Weapon("철검", 100, "일반", 10));
myInventory.addItem(new Potion("대형 체력 포션", 200, "희귀", 100));
// 인벤토리 표시
myInventory.showAllItems();
// 아이템 사용
cout << "\n=== 아이템 사용 ===" << endl;
myInventory.useItem(1); // 전설의 검
myInventory.useItem(2); // 체력 포션
myInventory.useItem(3); // 드래곤 갑옷
return 0;
}🔌 인터페이스 패턴
C++에는 인터페이스 키워드가 없지만, 순수 가상 함수만 있는 클래스로 구현합니다!
#include <iostream>
#include <vector>
using namespace std;
// 인터페이스 1 - 공격 가능
class IAttackable {
public:
virtual void attack() = 0;
virtual int getDamage() = 0;
virtual ~IAttackable() {}
};
// 인터페이스 2 - 방어 가능
class IDefendable {
public:
virtual void defend() = 0;
virtual int getDefense() = 0;
virtual ~IDefendable() {}
};
// 인터페이스 3 - 이동 가능
class IMovable {
public:
virtual void move(int x, int y) = 0;
virtual int getSpeed() = 0;
virtual ~IMovable() {}
};
// 전사 - 공격, 방어, 이동 모두 가능
class Warrior : public IAttackable, public IDefendable, public IMovable {
private:
string name;
int damage;
int defense;
int speed;
int x, y;
public:
Warrior(string n) : name(n), damage(20), defense(15), speed(5), x(0), y(0) {
cout << "⚔️ 전사 " << name << " 생성" << endl;
}
// IAttackable 구현
void attack() override {
cout << name << "의 검 공격! 💥" << endl;
}
int getDamage() override {
return damage;
}
// IDefendable 구현
void defend() override {
cout << name << "이(가) 방패로 막습니다! 🛡️" << endl;
}
int getDefense() override {
return defense;
}
// IMovable 구현
void move(int newX, int newY) override {
x = newX;
y = newY;
cout << name << "이(가) (" << x << ", " << y << ")로 이동 🏃" << endl;
}
int getSpeed() override {
return speed;
}
};
// 탑 - 공격과 방어만 가능 (이동 불가)
class Tower : public IAttackable, public IDefendable {
private:
string name;
int damage;
int defense;
public:
Tower(string n) : name(n), damage(30), defense(50) {
cout << "🏰 방어탑 " << name << " 건설" << endl;
}
// IAttackable 구현
void attack() override {
cout << name << "의 포탄 발사! 💣" << endl;
}
int getDamage() override {
return damage;
}
// IDefendable 구현
void defend() override {
cout << name << "의 방어막 작동! 🔰" << endl;
}
int getDefense() override {
return defense;
}
};
// 정찰병 - 이동만 가능 (공격, 방어 불가)
class Scout : public IMovable {
private:
string name;
int speed;
int x, y;
public:
Scout(string n) : name(n), speed(15), x(0), y(0) {
cout << "🏃 정찰병 " << name << " 생성" << endl;
}
void move(int newX, int newY) override {
x = newX;
y = newY;
cout << name << "이(가) 빠르게 (" << x << ", " << y << ")로 이동! 💨" << endl;
}
int getSpeed() override {
return speed;
}
};
// 전투 시스템
void performBattle(IAttackable* attacker, IDefendable* defender) {
cout << "\n⚔️ 전투 시작!" << endl;
attacker->attack();
cout << "데미지: " << attacker->getDamage() << endl;
defender->defend();
cout << "방어력: " << defender->getDefense() << endl;
int actualDamage = attacker->getDamage() - defender->getDefense();
if (actualDamage > 0) {
cout << "💥 " << actualDamage << " 데미지!" << endl;
} else {
cout << "🛡️ 공격이 막혔습니다!" << endl;
}
}
int main() {
cout << "=== 인터페이스 패턴 시스템 ===" << endl;
// 다양한 유닛 생성
Warrior warrior("아서");
Tower tower("수호탑");
Scout scout("정찰병1");
cout << "\n=== 유닛 동작 테스트 ===" << endl;
// 이동 가능한 유닛들
IMovable* movables[] = {&warrior, &scout};
for (IMovable* unit : movables) {
unit->move(10, 20);
}
// 전투 테스트
performBattle(&warrior, &tower);
performBattle(&tower, &warrior);
return 0;
}📊 추상 클래스 vs 인터페이스
| 특징 | 추상 클래스 | 인터페이스 |
|---|---|---|
| 목적 | 공통 기능 제공 | 계약/규약 정의 |
| 구현 | 일부 구현 가능 | 구현 없음 (순수 가상만) |
| 멤버 변수 | 가능 | 불가능 (또는 static만) |
| 다중 상속 | 제한적 | 자유롭게 가능 |
| 사용 시기 | is-a 관계 | can-do 관계 |
🏗️ 플러그인 시스템 예제
// 플러그인 인터페이스
class IPlugin {
public:
virtual void initialize() = 0;
virtual void execute() = 0;
virtual void shutdown() = 0;
virtual string getName() = 0;
virtual string getVersion() = 0;
virtual ~IPlugin() {}
};
// 구체적인 플러그인 구현
class AudioPlugin : public IPlugin {
public:
void initialize() override {
cout << "🔊 오디오 플러그인 초기화" << endl;
}
void execute() override {
cout << "🎵 오디오 처리 중..." << endl;
}
void shutdown() override {
cout << "🔇 오디오 플러그인 종료" << endl;
}
string getName() override { return "AudioPlugin"; }
string getVersion() override { return "1.0.0"; }
};
// 플러그인 매니저
class PluginManager {
private:
vector<IPlugin*> plugins;
public:
void registerPlugin(IPlugin* plugin) {
plugins.push_back(plugin);
cout << "✅ " << plugin->getName() << " v"
<< plugin->getVersion() << " 등록 완료" << endl;
}
void initializeAll() {
for (IPlugin* plugin : plugins) {
plugin->initialize();
}
}
void executeAll() {
for (IPlugin* plugin : plugins) {
plugin->execute();
}
}
void shutdownAll() {
for (IPlugin* plugin : plugins) {
plugin->shutdown();
}
}
};💡 핵심 정리
- 순수 가상 함수:
= 0으로 선언, 구현 없음 - 추상 클래스: 순수 가상 함수를 하나 이상 가진 클래스
- 인터페이스: 순수 가상 함수만 있는 클래스 (관례)
- 객체 생성 불가: 추상 클래스는 직접 객체 생성 불가
- 상속 강제: 자식 클래스가 반드시 구현해야 함
✅ 실습 체크리스트
🚀 다음 시간 예고
다음 시간에는 다중 상속과 가상 상속에 대해 알아볼 거예요!
- 다중 상속의 문제점
- 다이아몬드 문제
- 가상 상속으로 해결
“추상화로 더 유연한 설계를 만드세요! 📐”
Last updated on