예제 8. 동적 할당 문제
// ~~
switch (rand() % 3)
{
case 0:
{
p = new Knight();
p->_hp = 100;
p->_attack = 100;
break;
}
case 1:
{
// 여기서 같이 만들어준다
// 이런 저런 펫 정보 추가될 예정
Pet pet;
// Archer를 만들 때 pet 정보도 넘겨준다
p = new Archer(&pet);
p->_hp = 100;
p->_attack = 100;
break;
}
case 2:
{
p = new Mage();
p->_hp = 100;
p->_attack = 100;
break;
}
}
//~~ Exercise_8.cpp
이번엔 펫이 Archer(pet) 생성자를 통해 만들어진다.
#include "Archer.h"
#include "Pet.h"
Archer::Archer(Pet* pet) : _pet(pet)
{
}
Archer::Archer(int hp) : Player(hp)
{
}
Archer::~Archer()
{
delete _pet;
}
// Archer.cpp
스택 메모리 영역에 pet 객체가 생성된다.
pet의 주소를 아쳐에게 전달하고 아쳐가 제거될 때 pet이 제거되고 있다.
즉 지역 변수로 관리되고 있는 pet이 계속 지워지고 있는 문제이다.
{
Pet* pet = new Pet;
p = new Archer(pet);
p->_hp = 100;
p->_attack = 100;
break;
}
따라서 아쳐를 생성할 때 pet을 동적 할당하게 설정한다.
예제 9. 더블 프리(Double Free) 문제
int main()
{
srand(static_cast<unsigned int>(time(nullptr)));
Archer* archer = new Archer(new Pet());
archer->_hp = 100;
archer->_maxHp = 100;
archer->_attack = 20;
Knight* knight = new Knight();
knight->_hp = 150;
knight->_maxHp = 150;
knight->_attack = 100;
int damage = knight->GetAttackDamage();
archer->AddHp(-damage);
delete archer;
delete knight;
}
위에서 아쳐에게 펫을 만들어주고 기사와 싸우게 만든다.
#include "Archer.h"
#include "Pet.h"
Archer::Archer(Pet* pet) : _pet(pet)
{
}
Archer::Archer(int hp) : Player(hp)
{
}
Archer::~Archer()
{
if (_pet != nullptr)
delete _pet;
}
void Archer::AddHp(int value)
{
Player::AddHp(value);
if (IsDead())
{
delete _pet;
}
}
이후 데미지를 계산해서 죽으면 펫을 제거하고 아쳐 객체를 delete 하는 데에서 크래시가 발생한다.
아쳐가 죽을 때 pet이 제거되고 아쳐의 소멸자가 호출될 때 또 pet이 제거되기 때문이며 이는 더블 프리 문제이다.
void Archer::AddHp(int value)
{
Player::AddHp(value);
if (IsDead())
{
delete _pet;
_pet = nullptr;
}
}
따라서 포인터를 0으로 만들어주고 위의 Null Check를 통과하게 해서 더블 프리를 하지 않게 한다.
예제 10. Use-After-Free ; 잘못된 메모리 참조(투사체 문제)
Arrow* arrows[10] = {};
for (int i = 0; i < 10; i++)
{
// 기사를 타겟으로, 궁수의 공격력을 지닌 상태
Arrow* arrow = new Arrow(knight, archer->_attack);
arrows[i] = arrow;
}
for (int i = 0; i < 10; i++)
{
arrows[i]->AttackTarget();
// 기사가 죽었으면 소멸시켜준다
if (knight != nullptr)
{
if (knight->IsDead())
{
delete knight;
knight = nullptr;
}
}
delete arrows[i];
arrows[i] = nullptr;
}
화살이 8발 째 발사될 때 이미 기사는 제거된 상태(객체가 제거되어 메모리가 free된 상태)이다.
하지만 여전히 8발 째, 9발 째, 10발 째 화살이 원래 기사를 가리키고 있었기 때문에 발생하는 문제이다.
기사의 PrintInfo는 virtual이 붙어있기 때문에 가상 함수 테이블을 저장하고 있었다. 메모리에 할당된 기사의 영역이 제거되면서 화살이 그 영역을 가리키고 있었다.
따라서 기사가 죽었을 때 소멸시켜주는 부분을 삭제하고 반복문 이후에 아쳐와 기사를 제거해준다.
'기초 C++ 스터디 > 예제' 카테고리의 다른 글
9-5. STL - List 구현하기(실습) (0) | 2023.06.14 |
---|---|
9-3. STL - Vector 구현하기 (실습) (0) | 2023.06.14 |
7-2. 디버깅 예제 #5 ~ 7 (0) | 2023.06.07 |
7-1. 디버깅 예제 #1 ~ 4 (0) | 2023.06.07 |
7-0. 디버깅 연습 (0) | 2023.06.07 |