오븐 노트

[C++] 타입 변환 #4~5 본문

Develop/C++

[C++] 타입 변환 #4~5

오 븐 2023. 2. 15. 21:47
#include <iostream>
using namespace std;

class Knight
{
public:
    int _hp = 0;
};

class Item
{
public:
    Item()
    {
        cout << "Item()" << endl;
    }

    Item(int itemType) : _itemType(itemType)
    {
        cout << "Item(int itemType)" << endl;
    }

    Item(const Item& item)
    {
        cout << "Item(const Item&)" << endl;
    }

    // 어떠한 상속 관계가 있을 경우 해당 상속 관계의 최상위 부모의 소멸자에는 항상 virtual을 붙여야함
    // -> 상속 관계에 있는 자식 클래스들의 소멸자에도 virtual이 붙은 셈이 됨.
    // -> 결국 소멸자 호출 시 한번에 호출해도 알맞은 소멸자를 찾아가게 됨.
    virtual ~Item()
    {
        cout << "~Item()" << endl;
    }

    // 원본 객체의 타입에 따라서 호출되기를 원할 경우 사용하는 virtual (가상 함수)
    // 객체 자체의 virtual funtion table(가상 함수 테이블)을 생성 -> 지금 실행 되어야 하는 함수가 몇번째인지 알 수 있도록 관리 가능한 함수 테이블
    virtual void Test()
    {
        cout << "Test Item" << endl;
    }

public:
    int _itemType = 0;
    int _itemDbld = 0;

    char _dummy[4096] = {}; // 용량이 커진 클래스를 표현하기 위한 더미 데이터
};

enum ItemType
{
    IT_WEAPON = 1,
    IT_ARMOR = 2,
};

class Weapon : public Item
{
public:
    Weapon() : Item(IT_WEAPON)
    {
        cout << "Weapon()" << endl;
        _damage = rand() % 100;
    }

    ~Weapon()
    {
        cout << "~Weapon()" << endl;
    }

    virtual void Test()
    {
        cout << "Test Weapon" << endl;
    }

public:
    int _damage = 0;
};

class Armor : public Item
{
public:
    Armor() : Item(IT_ARMOR)
    {
        cout << "Armor()" << endl;
    }

    ~Armor()
    {
        cout << "~Armor()" << endl;
    }

public:
    int _defence = 0;
};

void TestItem(Item item)
{

}

void TestItemPtr(Item* item)
{
    item->Test();
}

int main()
{
    // 연관성이 없는 클래스 사이의 포인터 변환 테스트 ※매우 위험※
    {
        // Stack [ 주소 ] -> Heap { hp(4) }
        //Knight* knight = new Knight();

        // 암시적으로는 불가, 명시적으로는 가능
        // 하면 안되는 캐스팅의 예시
        //Item* item = (Item*)knight;
        //item->_itemType = 2;
        //item->_itemDbld = 1;
        //delete knight;
    }

    // 부모 -> 자식 변환 테스트 ※매우 위험※
    {
        //Item* item = new Item();

        //Weapon* weapon = (Weapon*)item;

        //delete item;
    }

    // 자식 -> 부모 변환 테스트
    {
        Weapon* weapon = new Weapon();

        Item* item = weapon;

        //TestItemPtr(item); // 가상 함수 테스트

        delete weapon;
    }

    // 명시적으로 타입 변환할 때는 항상 주의 해야함
    // 암시적으로 될 때는 안전한가?
    // -> 명시적 캐스팅을 하지 않으면 되는것인가?
    Item* inventory[20] = {};

    srand((unsigned int)time(nullptr));

    for (int i = 0; i < 20; i++)
    {
        int randValue = rand() % 2; // 0~1
        switch (randValue)
        {
        case 0:
            inventory[i] = new Weapon();
            break;
        case 1:
            inventory[i] = new Armor();
            break;
        }
    }

    for (int i = 0; i < 20; i++)
    {
        Item* item = inventory[i];
        if (item == nullptr)
            continue;

        if (item->_itemType == IT_WEAPON)
        {
            // 이런 경우 비교적 안전한 캐스팅
            Weapon* weapon = (Weapon*)item;
            cout << "Weapon Damage : " << weapon->_damage << endl;
        }
    }

    // ※※※ 매우 중요 ※※※
    for (int i = 0; i < 20; i++)
    {
        Item* item = inventory[i];
        if (item == nullptr)
            continue;

        // 최상위 부모의 소멸자에 가상 함수 테이블을 생성하여서 모든 자식 요소에 알맞은 소멸자가 선택되게 됨.
        delete item;
        // 그렇게 하지 않을 경우 귀찮아질뿐더러 실수하면 지워야할 메모리가 지워지지 않을 위험이 있다.
        //if (item->_itemType == IT_WEAPON)
        //{
        //    Weapon* weapon = (Weapon*)item;
        //    delete weapon;
        //}
        //else
        //{
        //    Armor* armor = (Armor*)item;
        //    delete armor;
        //}
    }

    // [결론]
    // - 포인터 vs 일반 타입 : 메모리 구조에 대한 차이를 반드시 숙지하여야함.
    // - 포인터 사이의 타입 변환(캐스팅)을 할 때는 매우 주의 필요.
    // ※※※※※※※※※※※※※※※※※※※※※※ 중요 ※※※※※※※※※※※※※※※※※※※※※※※※※
    // - 부모-자식 관계에서 부모 클래스의 소멸자에는 반드시 virtual을 붙여야함.
    // -- 함수를 재정의 하여도 특정 하나의 함수로만 실행되기 때문에 virtual을 붙여서 가상 함수 테이블을 생성 한 후 원본 객체가 어떤것으로 생성되었는지에 따라서 해당 함수를 찾아서 호출하도록 해야함.
    // ※※※※※※※※※※※※※※※※※※※※※※ 중요 ※※※※※※※※※※※※※※※※※※※※※※※※※

    return 0;
}

[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part1: C++ 프로그래밍 입문 - 인프런 | 강의
 
C++ 카테고리의 글은 인프런 Rookiss님의 강의를 공부하며 정리하는 내용입니다.
이미 알고 있는 내용도 다시 정리 되어있을 수 있습니다.

 

모든 글은 제가 공부하기 위해 작성합니다.

'Develop > C++' 카테고리의 다른 글

[C++] 캐스팅 4총사  (0) 2023.03.03
[C++] 얕은 복사 VS 깊은 복사 #1~2  (0) 2023.02.28
[C++] 타입 변환 #3  (0) 2023.02.15
[C++] 타입 변환 #1~2  (0) 2023.02.13
[C++] 동적할당 #3  (0) 2023.02.08