TIL

[C++] 클래스 상속과 템플릿의 조화

think95592 2026. 5. 11. 20:51

1. 템플릿(Template): 타입을 초월한 설계도

템플릿은 데이터 타입만 비워둔 '미완성 설계도'입니다. 컴파일 시점에 사용자가 넣은 타입에 맞춰 실제 코드가 생성됩니다.

  • 왜 쓰는가?: Player 공격, Monster 공격 함수를 따로 만들지 않고, Battack(T& give, U& take) 하나로 모든 전투 상황을 처리하기 위함입니다.
  • 주의점: 템플릿 함수 내부에서 target->Inventory를 참조한다면, 인자로 들어오는 모든 타입(T, U)이 반드시 Inventory 멤버를 가지고 있어야 합니다.

2. 클래스 상속과 생성자의 비밀

부모와 자식 관계에서 메모리가 어떻게 생성되는지 이해하는 것이 오늘 에러 해결의 핵심이었습니다.

  • 부모 없는 자식 없다: 자식 클래스(Monster)의 생성자가 호출될 때, 부모 클래스(Entity)의 생성자가 가장 먼저 실행되어야 합니다.
  • 초기화 리스트: 부모에게 기본 생성자가 없다면, 자식 생성자에서 : Entity(n, h, ...)와 같이 부모에게 필요한 인자를 명시적으로 전달해야 합니다.

3. 멤버 변수로서의 객체와 기본 생성자

클래스 내부에 다른 객체(Item dropitem)를 변수로 들고 있을 때의 규칙입니다.

  • 전방 선언의 한계: struct Item;처럼 이름만 알려주면 포인터(Item*)는 만들 수 있지만, 실제 변수(Item dropitem)는 크기를 알 수 없어 선언이 불가능합니다. (정의를 위로 올려야 함)
  • 기본 생성자의 필요성: Entity를 만들 때 아이템을 바로 넣어주지 않는다면, Item 구조체에 내용이 비어있는 Item() {} 생성자가 있어야 컴파일러가 안심하고 공간을 확보합니다.

4. const와 함수의 자격 증명 (Qualifier)

오늘 가장 까다로웠던 "type qualifiers that are not compatible" 에러의 정체입니다.

  • const 객체: "내용을 절대 바꾸지 않겠다"고 약속된 객체입니다. (const auto& item)
  • const 함수: void PrintInfo() const { ... }처럼 뒤에 const가 붙은 함수만 const 객체에서 호출할 수 있습니다.
  • 결론: 데이터를 출력만 하는 함수는 무조건 뒤에 const를 붙이는 습관을 들여야 합니다.

5. [실전 예제] 오늘 완성한 RPG 기초 구조

오늘 에러를 모두 수정하고 다듬은 최종적인 클래스 설계입니다.

C++
 
#include <iostream>
#include <vector>
#include <string>

using namespace std;

struct Item {
    string name;
    int price;
    Item() : name("없음"), price(0) {} // 기본 생성자: Entity 생성 시 필요
    Item(string n, int p) : name(n), price(p) {}

    void PrintInfo() const { // const 보증서: 인벤토리 출력 시 필요
        cout << name << " (" << price << "G)" << endl;
    }
};

class Entity {
public:
    string name;
    int hp, power, defence;
    vector<Item> Inventory; // 모든 개체는 가방을 가질 수 있음 (Battack 에러 방지)
    Item dropitem;         // 몬스터가 죽을 때 줄 아이템

    Entity(string n, int h, int p, int d) 
        : name(n), hp(h), power(p), defence(d) {}
    
    virtual ~Entity() {} // 가상 소멸자: 상속 시 필수!
};

class Monster : public Entity {
public:
    // 부모 생성자 호출 후, 본인만의 dropitem 저장
    Monster(string n, int h, int p, int d, Item i) 
        : Entity(n, h, p, d) { dropitem = i; }
};

💡 오늘의 깨달음 (복습 포인트)

  1. 에러 메시지를 읽는 법: not a member of는 클래스 설계도에 해당 변수가 없다는 뜻이고, qualifiers not compatible은 const 약속이 깨졌다는 뜻이다.
  2. 공통 멤버는 부모에게: 플레이어와 몬스터가 공통으로 하는 행동(공격, 방어, 사망)이나 속성(이름, 체력, 인벤토리)은 부모 클래스인 Entity에 두는 것이 템플릿 함수를 쓸 때 훨씬 편하다.
  3. 메모리 해제의 책임: new로 동적 할당을 했다면, 그 객체를 가리키는 포인터가 사라지기 전에 반드시 delete를 호출하자.