[C++] 타입 변환 4: 포인터
타입 변환 4: 포인터
인프런 Rookiss님의 'Part1: C++ 프로그래밍 입문' 강의를 기반으로 정리한 필기입니다.
😎[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part1: C++ 프로그래밍 입문 강의 들으러 가기!
공통 부분
#include <iostream> using namespace std; // 타입 변환 (포인터) class Knight{ public: int _hp = 0; }; class Item{ public: Item() { cout << "Item()" << endl; } Item(int itemType) : _itemType(itemType) { } Item(const Item& item) { cout << "Item(const Item&)" << endl; } ~Item() { cout << "~Item()" << endl; } public: int _itemType = 0; int _itemDbld = 0; char _cummy[4096] = {}; // 이런 저런 정보들로 인해 비대해진 아이템 클래스를 나타내기 위해 표시 }; enum ItemType { IT_WEAPON = 1, IT_ARMOR =2, }; class Weapon : public Item { public: Weapon() : Item(IT_WEAPON) { _itemType = 1; cout << "Weapon()" << endl; } ~Weapon() { cout << "~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; };
연관성이 없는 클래스 사이의 포인터 변환 테스트
int main() { // Stack [ 주소 ] -> Heap [ _hp(4) ] Knight* knight = new Knight(); // 암시적으로는 NO // 명시적으로는 OK // Stack [ 주소 ] Item* item = (Item*)knight; item->_itemType = 2; // 4byte로 유효한 정보를 건드리기 때문에 정상적으로 변경된다. item->_itemDbld = 1; // 데이터의 유효 범위를 벋어나서 문제가 된다. delete knight; }
delete knight; 하지 않는 경우, 컴파일은 통과되지만 item->_itemDbld = 1;가 문제가 된 상태로 넘어갈 수 있다.
이 경우, 추후에 나비효과가 되어 돌아올 수 있다.
다운 캐스트: 부모 -> 자식 변환 (Base class -> Derived class)
부모 -> 자식 변환은 명시적으로 사용해야 통과된다.
int main() { Item* item = new Item(); // [ [ Item ] ] // [ _damage ] Weapon* weapon = (Weapon*)item; // 위험한 상황, 잘못된 코드 Weapon->_damage = 10; // 잘못된 메모리를 건드림, 잘못된 코드 delete item; }
Object Slicing 을 조심해야 한다.
메모리의 크기가 큰 것에서 작은 것으로 변환하는 경우, 엉뚱한 메모리를 건드릴 수 있다.
위의 경우, item은 weapon보다 메모리의 크기가 크다. (*Weapon)item과 같이 명시적 변화를 하면 엉뚱한 메모리를 건드릴 수 있다.
명시적으로 타입 변환할 때는 항상 항상 조심해야 한다!
자식 -> 부모 변환 테스트
int main() { // [ [ Item ] ] // [ _damage ] Weapon* weapon = new Weapon(); // 암시적으로도 된다! Item* item = weapon; // Item* item = (item*) weapon; // (item*) 부분이 생략되어있다 delete weapon; }
조심할 사항들
명시적으로 타입 변환할 때는 항상 항상 조심해야 한다!
암시적으로 될 때는 안전하다?
-> 평생 명시적으로 타입 변환(캐스팅)은 안 하면 되는거 아닌가?
int main() { 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; } } }
실행화면

int main() { for (int i = 0; i < 20; i++) { Item* item = inventory[i]; if (item == nullptr) continue; //** Virtual 함수 변경으로 delete item; 하나로 해결 if (item->_itemType == IT_WEAPON) { Weapon* weapon = (Weapon*)item; delete weapon; } else { Armor* armor = (Armor*)item; delete armor; } delete item; } // [결론] // - 포인터 vs 일반 타입 : 차이를 이해하자 // - 포인터 사이의 타입 변환(캐스팅)을 할 때는 매우 매우 조심해야 한다! // - 부모-자식 관계에서 부모 클래스의 소멸자에는 까먹지 말고 virtual을 붙이자!!! // 코딩 테스트와 면접 단골문제! return 0; }

포인터 vs 일반 타입 : 차이를 이해하자
- 포인터 사이의 타입 변환(캐스팅)을 할 때는 매우 매우 조심해야 한다!
- 부모-자식 관계에서 부모 클래스의 소멸자에는 까먹지 말고 virtual을 붙이자!!!
전체 코드
더보기
#include <iostream> using namespace std; // 타입 변환 (포인터) class Knight { public: int _hp = 0; }; class Item { public: Item() { cout << "Item()" << endl; } Item(int itemType) : _itemType(itemType) { } Item(const Item& item) { cout << "Item(const Item&)" << endl; } ~Item() { cout << "~Item()" << endl; } public: int _itemType = 0; int _itemDbld = 0; char _cummy[4096] = {}; // 이런 저런 정보들로 인해 비대해진 아이템 클래스를 나타내기 위해 표시 }; enum ItemType { IT_WEAPON = 1, IT_ARMOR =2, }; class Weapon : public Item { public: Weapon() : Item(IT_WEAPON) { _itemType = 1; cout << "Weapon()" << endl; } ~Weapon() { cout << "~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; }; int main() { // 연관성이 없는 클래스 사이의 포인터 변환 테스트 { // Stack [ 주소 ] -> Heap [ _hp(4) ] Knight* knight = new Knight(); // 암시적으로는 NO // 명시적으로는 OK // Stack [ 주소 ] // tem* item = (Item*)knight; // item->_itemType = 2; // item->_itemDbld = 1; delete knight; } // 부모 -> 자식 변환 테스트 { Item* item = new Item(); // [ [ Item ] ] // [ _damage ] Weapon* weapon = (Weapon*)item; // 위험한 상황, 잘못된 코드 Weapon->_damage = 10; // 잘못된 메모리를 건드림, 잘못된 코드 delete item; } // 자식 -> 부모 변환 테스트 { // [ [ Item ] ] // [ _damage ] Weapon* weapon = new Weapon(); // 암시적으로도 된다! Item* item = weapon; // Item* item = (item*) weapon; // (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; } } return 0; }
'⭐ Programming > C++' 카테고리의 다른 글
[C++] 얕은 복사 vs 깊은 복사 2 (0) | 2022.04.10 |
---|---|
[C++] 얕은 복사 vs 깊은 복사 1 (0) | 2022.04.09 |
[C++] 타입 변환 3: 포인터 타입 변환 (0) | 2022.04.08 |
[C++] 타입 변환 2: 참조 타입 변환 (0) | 2022.04.08 |
[C++] 타입 변환 1: 값, 타입 변환 / 암시적, 명시적 변환 (0) | 2022.04.08 |
댓글
이 글 공유하기
다른 글
-
[C++] 얕은 복사 vs 깊은 복사 2
[C++] 얕은 복사 vs 깊은 복사 2
2022.04.10 -
[C++] 얕은 복사 vs 깊은 복사 1
[C++] 얕은 복사 vs 깊은 복사 1
2022.04.09 -
[C++] 타입 변환 3: 포인터 타입 변환
[C++] 타입 변환 3: 포인터 타입 변환
2022.04.08 -
[C++] 타입 변환 2: 참조 타입 변환
[C++] 타입 변환 2: 참조 타입 변환
2022.04.08
댓글을 사용할 수 없습니다.