Skip to Content
💻 코리아IT아카데미 신촌 - 프로그래밍 학습 자료
C++ 프로그래밍Unit 12: 상속과 다형성Topic 4: 다중 상속과 가상 상속

Topic 4: 다중 상속과 가상 상속 🔀

🎯 학습 목표

  • 다중 상속의 개념과 활용법을 이해할 수 있다
  • 다이아몬드 문제를 인식하고 해결할 수 있다
  • 가상 상속의 원리를 이해할 수 있다
  • 다중 상속의 장단점을 설명할 수 있다

🎭 다재다능한 사람으로 이해하는 다중 상속

다중 상속여러 재능을 가진 사람과 같습니다!

엔터테이너의 다중 재능 🌟

// 가수의 능력 class Singer { void sing() { cout << "노래하기 🎤" << endl; } }; // 댄서의 능력 class Dancer { void dance() { cout << "춤추기 💃" << endl; } }; // 아이돌 = 가수 + 댄서 class Idol : public Singer, public Dancer { // 노래도 할 수 있고 // 춤도 출 수 있음! void perform() { sing(); // Singer로부터 dance(); // Dancer로부터 } };

📝 다중 상속 기본 문법

#include <iostream> #include <string> using namespace std; // 첫 번째 부모 클래스 class Student { protected: string school; int grade; public: Student(string s, int g) : school(s), grade(g) { cout << "📚 학생 정보 등록" << endl; } void study() { cout << school << " " << grade << "학년 학생이 공부합니다 📖" << endl; } void showStudentInfo() { cout << "학교: " << school << ", 학년: " << grade << endl; } }; // 두 번째 부모 클래스 class Athlete { protected: string sport; int level; public: Athlete(string s, int l) : sport(s), level(l) { cout << "🏃 운동선수 정보 등록" << endl; } void train() { cout << sport << " 레벨 " << level << " 선수가 훈련합니다 💪" << endl; } void showAthleteInfo() { cout << "종목: " << sport << ", 레벨: " << level << endl; } }; // 다중 상속 - 학생이면서 운동선수 class StudentAthlete : public Student, public Athlete { private: string name; public: // 두 부모의 생성자 모두 호출 StudentAthlete(string n, string sch, int g, string sp, int l) : Student(sch, g), Athlete(sp, l), name(n) { cout << "🌟 학생 선수 " << name << " 등록 완료!" << endl; } void dailySchedule() { cout << "\n=== " << name << "의 하루 일과 ===" << endl; cout << "오전: "; study(); // Student로부터 cout << "오후: "; train(); // Athlete로부터 } void showFullInfo() { cout << "\n=== " << name << "의 정보 ===" << endl; showStudentInfo(); // Student로부터 showAthleteInfo(); // Athlete로부터 } }; int main() { cout << "=== 다중 상속 기본 예제 ===" << endl; StudentAthlete kim("김철수", "코딩고등학교", 2, "축구", 3); kim.dailySchedule(); kim.showFullInfo(); return 0; }

💎 다이아몬드 문제 (Diamond Problem)

문제 상황 시각화 💎

Animal / \ / \ Mammal Bird \ / \ / FlyingBat

다이아몬드 문제 발생

#include <iostream> using namespace std; // 최상위 클래스 class Device { protected: int powerLevel; public: Device() : powerLevel(100) { cout << "📱 Device 생성 (전력: " << powerLevel << "%)" << endl; } void setPower(int level) { powerLevel = level; cout << "전력 설정: " << powerLevel << "%" << endl; } void showPower() { cout << "현재 전력: " << powerLevel << "%" << endl; } }; // 중간 클래스 1 class Phone : public Device { public: Phone() { cout << "📞 Phone 기능 추가" << endl; } void makeCall() { cout << "전화 걸기 📞" << endl; } }; // 중간 클래스 2 class Camera : public Device { public: Camera() { cout << "📷 Camera 기능 추가" << endl; } void takePhoto() { cout << "사진 촬영 📸" << endl; } }; // 문제 발생! - Device가 두 번 상속됨 class SmartPhone : public Phone, public Camera { public: SmartPhone() { cout << "📱 SmartPhone 생성" << endl; } void useApps() { cout << "앱 실행 📲" << endl; } }; void diamondProblemDemo() { cout << "=== 다이아몬드 문제 시연 ===" << endl; SmartPhone myPhone; // Device 생성자가 두 번 호출됨! // 모호성 문제! // myPhone.setPower(50); // 에러! Phone::setPower? Camera::setPower? // 명시적으로 지정해야 함 myPhone.Phone::setPower(50); myPhone.Camera::setPower(80); // 두 개의 다른 powerLevel! myPhone.Phone::showPower(); // 50% myPhone.Camera::showPower(); // 80% }

✨ 가상 상속으로 해결!

#include <iostream> using namespace std; // 최상위 클래스 class Device { protected: int powerLevel; string deviceID; public: Device() : powerLevel(100), deviceID("DEV001") { cout << "📱 Device 생성 (ID: " << deviceID << ")" << endl; } virtual ~Device() { cout << "📱 Device 소멸" << endl; } void setPower(int level) { powerLevel = level; cout << "전력 설정: " << powerLevel << "%" << endl; } void showInfo() { cout << "Device ID: " << deviceID << ", 전력: " << powerLevel << "%" << endl; } }; // 가상 상속 사용! ⭐ class Phone : virtual public Device { public: Phone() { cout << "📞 Phone 기능 추가" << endl; } void makeCall() { cout << "전화 걸기 📞 (전력 사용: -5%)" << endl; powerLevel -= 5; } }; // 가상 상속 사용! ⭐ class Camera : virtual public Device { public: Camera() { cout << "📷 Camera 기능 추가" << endl; } void takePhoto() { cout << "사진 촬영 📸 (전력 사용: -3%)" << endl; powerLevel -= 3; } }; // 이제 Device는 한 번만 상속됨! class SmartPhone : public Phone, public Camera { private: string model; public: SmartPhone(string m = "Galaxy") : model(m) { cout << "📱 SmartPhone " << model << " 생성 완료!" << endl; } void useApps() { cout << "앱 실행 📲 (전력 사용: -10%)" << endl; powerLevel -= 10; } void showSmartPhoneInfo() { cout << "\n=== " << model << " 정보 ===" << endl; showInfo(); // 이제 모호성 없음! } }; void virtualInheritanceDemo() { cout << "=== 가상 상속 해결책 ===" << endl; SmartPhone myPhone("iPhone"); // Device는 한 번만 생성됨! myPhone.showSmartPhoneInfo(); // 모호성 없이 직접 호출 가능! myPhone.setPower(100); // 각 기능 사용 myPhone.makeCall(); myPhone.takePhoto(); myPhone.useApps(); myPhone.showSmartPhoneInfo(); } int main() { // 다이아몬드 문제 diamondProblemDemo(); cout << "\n" << string(50, '=') << "\n" << endl; // 가상 상속 해결책 virtualInheritanceDemo(); return 0; }

🎮 실전 예제: 게임 캐릭터 능력 시스템

#include <iostream> #include <string> #include <vector> using namespace std; // 기본 능력 인터페이스들 class IMagicUser { public: virtual void castSpell() = 0; virtual int getMana() = 0; virtual ~IMagicUser() {} }; class IWeaponUser { public: virtual void attackWithWeapon() = 0; virtual int getStrength() = 0; virtual ~IWeaponUser() {} }; class IHealer { public: virtual void heal(int amount) = 0; virtual int getHealPower() = 0; virtual ~IHealer() {} }; // 기본 캐릭터 클래스 class Character { protected: string name; int level; int hp; int maxHp; public: Character(string n, int lv = 1) : name(n), level(lv), hp(100), maxHp(100) { cout << "🎮 캐릭터 생성: " << name << endl; } virtual ~Character() {} virtual void showInfo() { cout << "\n=== " << name << " ===" << endl; cout << "레벨: " << level << endl; cout << "HP: " << hp << "/" << maxHp << endl; } void takeDamage(int damage) { hp -= damage; if (hp < 0) hp = 0; cout << name << "이(가) " << damage << " 데미지 받음! (HP: " << hp << ")" << endl; } }; // 전사 - 무기 사용 class Warrior : virtual public Character, public IWeaponUser { protected: int strength; public: Warrior(string n) : Character(n), strength(20) { maxHp = 150; hp = maxHp; cout << "⚔️ 전사 클래스 획득" << endl; } void attackWithWeapon() override { cout << name << "의 검 공격! 💥 (데미지: " << strength << ")" << endl; } int getStrength() override { return strength; } }; // 마법사 - 마법 사용 class Mage : virtual public Character, public IMagicUser { protected: int mana; int maxMana; int spellPower; public: Mage(string n) : Character(n), mana(100), maxMana(100), spellPower(30) { maxHp = 70; hp = maxHp; cout << "🧙 마법사 클래스 획득" << endl; } void castSpell() override { if (mana >= 20) { cout << name << "의 파이어볼! 🔥 (데미지: " << spellPower << ")" << endl; mana -= 20; } else { cout << "마나 부족! (현재: " << mana << ")" << endl; } } int getMana() override { return mana; } }; // 성직자 - 치유 능력 class Priest : virtual public Character, public IHealer, public IMagicUser { protected: int faith; int healPower; int mana; public: Priest(string n) : Character(n), faith(100), healPower(25), mana(80) { maxHp = 80; hp = maxHp; cout << "✨ 성직자 클래스 획득" << endl; } void heal(int amount) override { hp += amount; if (hp > maxHp) hp = maxHp; cout << name << "의 치유! 💚 (HP +" << amount << ")" << endl; } void castSpell() override { if (mana >= 15) { cout << name << "의 신성 마법! ✨ (데미지: 20)" << endl; mana -= 15; } } int getHealPower() override { return healPower; } int getMana() override { return mana; } }; // 팔라딘 - 전사 + 성직자 (다중 상속!) class Paladin : public Warrior, public Priest { public: Paladin(string n) : Character(n), Warrior(n), Priest(n) { maxHp = 120; hp = maxHp; strength = 15; healPower = 20; cout << "🛡️ 팔라딘 클래스 획득 (전사 + 성직자)" << endl; } // 고유 스킬 void holyStrike() { cout << name << "의 신성한 일격! ⚔️✨" << endl; cout << "물리 데미지: " << strength << " + 신성 데미지: 10" << endl; } void showInfo() override { Character::showInfo(); cout << "근력: " << strength << endl; cout << "신앙: " << faith << endl; cout << "마나: " << mana << endl; } }; int main() { cout << "=== 다중 클래스 시스템 ===" << endl; // 팔라딘 생성 (다중 상속) Paladin paladin("아서"); cout << "\n=== 팔라딘 능력 테스트 ===" << endl; // Warrior의 능력 paladin.attackWithWeapon(); // Priest의 능력 paladin.heal(30); paladin.castSpell(); // Paladin 고유 능력 paladin.holyStrike(); // 정보 출력 paladin.showInfo(); cout << "\n=== 인터페이스를 통한 다형성 ===" << endl; // 다형성 활용 IWeaponUser* weaponUser = &paladin; IHealer* healer = &paladin; IMagicUser* magicUser = &paladin; cout << "무기 사용자로서: "; weaponUser->attackWithWeapon(); cout << "치유사로서: "; healer->heal(20); cout << "마법 사용자로서: "; magicUser->castSpell(); return 0; }

⚖️ 다중 상속의 장단점

장점 ✅

  • 유연성: 여러 클래스의 기능 조합
  • 코드 재사용: 기존 클래스들을 활용
  • 현실 모델링: 복잡한 관계 표현 가능

단점 ❌

  • 복잡성: 이해하기 어려움
  • 다이아몬드 문제: 모호성 발생
  • 유지보수: 디버깅이 어려움

📋 가상 상속 사용 지침

언제 사용할까? 🤔

  1. 다이아몬드 구조가 생길 때
  2. 공통 조상을 한 번만 상속받고 싶을 때
  3. 인터페이스 다중 상속 시

주의사항 ⚠️

// 가상 상속 시 생성자 호출 순서 class A { }; class B : virtual public A { }; class C : virtual public A { }; class D : public B, public C { // D의 생성자가 A의 생성자를 직접 호출! };

💡 핵심 정리

  • 다중 상속: 여러 클래스로부터 상속받기
  • 다이아몬드 문제: 공통 조상이 중복 상속되는 문제
  • 가상 상속: virtual 키워드로 중복 상속 방지
  • 인터페이스 활용: 순수 가상 함수로 안전한 다중 상속
  • 신중한 사용: 복잡성을 고려하여 필요시에만 사용

✅ 실습 체크리스트

🎉 Unit 12 완료!

축하합니다! 상속과 다형성을 완벽히 마스터했습니다! 🎊

이번 Unit에서 배운 것들:

  • ✅ 상속의 기본 개념과 is-a 관계
  • ✅ 가상 함수와 동적 바인딩
  • ✅ 추상 클래스와 인터페이스 설계
  • ✅ 다중 상속과 가상 상속

다음 Unit 예고

Unit 13: 템플릿과 STL

  • 함수 템플릿과 클래스 템플릿
  • STL 컨테이너
  • STL 알고리즘
  • 스마트 포인터

“상속과 다형성으로 객체지향의 진정한 힘을 발휘하세요! 🚀”

Last updated on