Notice
Recent Posts
Recent Comments
Link
오븐 노트
[C++] 타입 변환 #4~5 본문
#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 |