Player p = k1;에서 왜 정보가 사라지는지(잘림) 설명할 수 있다.Player*로 관리하면 무엇이 되고, 무엇이 안 되는지(접근 한계) 설명할 수 있다.class Knight : public Player → Knight가 Player를 상속.class Player {
public:
void Move() {}
void Die() {}
protected:
int _hp;
int _attack;
int _defence;
};
class Mage : public Player {
public:
int _mp;
};
int main() {
Mage m1;
m1._hp = 10; // Player로부터 상속받은 _hp 사용
}
실전에서는 멤버 변수를 무조건 public으로 열기보다, 최소
protected/private로 감추고(은닉),
필요한 인터페이스(함수)로만 조작하게 만드는 방향이 유지보수에 유리합니다.
┌─────────────────────────────────────────────────────────────────────────────┐
│ "Knight is a Player?" → Yes → 상속 사용 ✅ │
│ "Knight is an Inventory?" → No → 상속 사용 ❌ │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Is-A (상속 O) Has-A (상속 X, 멤버로 포함) │
│ │
│ class Knight : public Player class Player { │
│ // Knight는 Player의 일종 Inventory _inventory; ← 멤버 변수로 소지 │
│ // HP, Attack 등 물려받음 // Player "가진" Inventory │
│ }; │
│ │
│ Knight ──is_a──► Player Player ──has_a──► Inventory │
│ (기사는 플레이어의 일종) (플레이어는 인벤토리를 가짐) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
| 관계 | 설명 | 예시 |
|---|---|---|
| Is-A | 자식이 부모의 일종일 때 → 상속 적절 | Knight is-a Player |
| Has-A | 객체를 포함(소유)하고 있을 때 → 포함(Composition) | Player has-a Inventory |
Player* p1 = &k1 → 부모 타입으로 참조 가능.Player p1 = k1) → 잘림(object slicing). 데이터 손실.┌─────────────────────────────────────────────────────────────────────────────┐
│ ❌ 값 대입 (잘림 발생) ✅ 포인터 (참조, 손실 없음) │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Knight k1 Player p1 Player* p1 = &k1 │
│ ┌─────────────────┐ ┌───────────┐ ┌─────────┐ │
│ │ _hp │ _attack│ │ _hp │ │ 0x100 │ ──────► k1 │
│ │ _defence│_stamina│ 대입 │ _attack │ 잘림! │ (8byte) │ 전체 │
│ │ (Player)│(Knight)│ ───► │ _defence │ _stamina │ │ │
│ └─────────────────┘ └───────────┘ 없어짐 └─────────┘ │
│ 큰 박스 작은 박스 포인터는 주소만 │
│ p1 = k1 → 작은 박스에 담음 데이터 손실! 가리키므로 손실 없음 │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
void Fight(Player* p1, Player* p2) {
p1->_hp -= p2->_attack;
}
int main() {
Knight k1;
Mage m1;
Fight(&k1, &m1); // 자동 업캐스팅 발생
}
⚠️ 주의:
Player*로는 “Player에 정의된 것만” 접근 가능합니다.p1->_mp 같은 Mage 전용 멤버는 접근할 수 없습니다.graph TB
GameObject[GameObject]
Creature[Creature]
Player[Player]
Monster[Monster]
NPC[NPC]
Pet[Pet]
Projectile[Projectile]
Arrow[Arrow]
Fireball[Fireball]
Item[Item]
Weapon[Weapon]
Sword[Sword]
Bow[Bow]
Armor[Armor]
Consumable[Consumable]
GameObject --> Creature
GameObject --> Projectile
GameObject --> Item
Creature --> Player
Creature --> Monster
Creature --> NPC
Creature --> Pet
Projectile --> Arrow
Projectile --> Fireball
Item --> Weapon
Item --> Armor
Item --> Consumable
Weapon --> Sword
Weapon --> Bow
GameObject
├─ Creature
│ ├─ Player
│ ├─ Monster
│ ├─ NPC
│ └─ Pet
├─ Projectile
│ ├─ Arrow
│ └─ Fireball
└─ Item
├─ Weapon
│ ├─ Sword
│ ├─ Bow
│ └─ Lance
├─ Armor
└─ Consumable
| 상속 방식 | 부모의 public | 부모의 protected | 부모의 private |
|---|---|---|---|
public 상속 | public 유지 | protected 유지 | 접근 불가 |
protected 상속 | protected로 변경 | protected 유지 | 접근 불가 |
private 상속 | private로 변경 | private로 변경 | 접근 불가 |
✅ 실무에서는 대부분 public 상속 사용.
Player p = k1;에서 “잘림”이 일어나는 이유는?is-a가 아닌데 상속으로 만들면 어떤 문제가 생길까?Player*)로 관리할 때, 자식 고유 기능을 바로 쓰기 어려운 이유는?