이 Step에서 다루는 것

  • Item* 하나로 Weapon/Armor를 받아도 “실제 타입 출력”이 되는 이유
  • 해결 우선순위: 가상 함수(다형성) 우선, 캐스팅은 최소화
  • 캐스팅이 필요한 상황에서의 안전 규칙(dynamic_cast 또는 타입 체크 후 변환)

학습 목표

  • item->PrintInfo()만으로도 타입별 출력이 가능한지 설명할 수 있다.
  • “공통 행동은 가상 함수로, 타입 전용 데이터는 제한적 캐스팅으로”라는 기준을 적용할 수 있다.

문제: 부모 포인터로는 자식 전용 멤버를 직접 못 쓴다

  • Item* item으로 받으면 Weapon::GetDamage() 같은 자식 전용 멤버는 바로 접근할 수 없습니다.
  • 하지만 출력 요구사항은 “Weapon이면 공격력, Armor면 방어력”처럼 타입별로 달라집니다.

해결 1(권장): 가상 함수로 동작을 객체 내부로 이동

핵심 아이디어:

  • 부모에 virtual PrintInfo()를 선언하고
  • 자식이 override로 각자 구현하면
  • 호출자는 타입 분기 없이 item->PrintInfo()만 호출하면 됩니다.
#include <iostream>

class Item {
public:
    virtual ~Item() = default;

    virtual void PrintInfo() const
    {
        std::cout << "[아이템 공통 정보]\n";
    }
};

class Weapon : public Item {
public:
    void PrintInfo() const override
    {
        Item::PrintInfo(); // 필요하면 부모 정보 먼저 출력
        std::cout << "[공격력] : " << _damage << '\n';
    }

private:
    int _damage = 10;
};

class Armor : public Item {
public:
    void PrintInfo() const override
    {
        Item::PrintInfo();
        std::cout << "[방어력] : " << _defence << '\n';
    }

private:
    int _defence = 5;
};

포인트:

  • override선언부(클래스 내부)에서 사용합니다.
  • 출력처럼 객체 상태를 바꾸지 않는 함수는 const를 붙이는 습관이 좋습니다.

해결 2(제한적): 캐스팅은 “정말 필요할 때만”

타입 전용 데이터를 꼭 꺼내야 한다면 캐스팅이 필요할 수 있습니다.
이때는 C-Style 캐스팅보다 안전 장치를 둔 방식이 좋습니다.

void PrintExtra(const Item* item)
{
    if (const Weapon* w = dynamic_cast<const Weapon*>(item)) {
        // Weapon 전용 처리
        // std::cout << w->GetDamage() << '\n';
    } else if (const Armor* a = dynamic_cast<const Armor*>(item)) {
        // Armor 전용 처리
        // std::cout << a->GetDefence() << '\n';
    }
}

실전 기준:

  • 공통 행동: 가상 함수(다형성)로 해결
  • 타입 전용 데이터 추출: 캐스팅을 최소 범위에서만 사용

체크 질문 (스스로 답해보기)

  • Item* item에 Weapon이 들어 있을 때 item->PrintInfo()가 Weapon 버전을 호출하는 이유는?
  • 캐스팅보다 가상 함수를 먼저 고려해야 하는 이유는?
  • 캐스팅이 꼭 필요할 때 C-Style보다 dynamic_cast를 선호하는 이유는?

profile
李家네_공부방

0개의 댓글