인프런 커뮤니티 질문&답변

igbtkd님의 프로필 이미지
igbtkd

작성한 질문수

[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part1: C++ 프로그래밍 입문

타입 변환 #5

안녕하세요! virtual 소멸자에 대해서 질문드려요!

작성

·

363

1

안녕하세요. 좋은 강의 항상 감사드립니다. 
덕분에 C# 부터 시작해서 C++까지 즐겁게 배우고 있는 중입니다. :)

다름이 아니라 질문드리고 싶은건 상위 클래스 소멸자에 virtual을 붙이지 않았을 때 메모리 Leak이 발생하는 지 테스트를 진행했는데 crtdbg를 사용해서 메모리 누수 체크를 해보니 누수된 메모리가 나오지 않았습니다.
그래서 성능 프로파일러에서 힙 프로파일링 옵션을 켠 뒤에 두 상황에 대해서 아래의 사진과 같이 비교를 해봤을 때 동일한 결과가 나왔습니다. 
이 상황에서 메모리 누수가 발생하지 않는 것인지
못찾아내고 있는 것인지 궁금합니다!

#include <iostream>
#include <iomanip>
#include <crtdbg.h>

#if _DEBUG
#define new new(_NORMAL_BLOCK,__FILE__,__LINE__)
#define malloc_d(s) _malloc_dbg(s, _NORMAL_BLOCK,__FILE__,__LINE__)
#endif

using namespace std;

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

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

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

	// 상위 개념에는 virtual을 붙여야 함
	~Item()
	{
		cout << "~Item()" << endl;
	}

public:
	int _itemType = 0;
	int _itemDbId = 0;

	char _dummy[4096] = {}; // 이런 저런 정보들로 인해 비대해진 데이터라 가정
};

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

class Weapon : public Item
{
public:
	Weapon() : Item(IT_WEAPON)
	{
		cout << "Weapon()" << endl;
		_attack = rand() % 1000;

	}

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

	}

public:
	int _attack = 0;
};

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

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

public:
	int _defence = 0;
};

int main()
{
        // 1번 스냅샷
	Item* inventory[20] = {};

	srand(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];
		// 포인터 사용할 때는 항상 null 체크 필수
		if (item == nullptr)
			continue;

		if (item->_itemType == IT_WEAPON)
		{
			Weapon* weapon = (Weapon*)item;
			cout << "Weapon Damage : " << weapon->_attack << endl;
		}
	}

        // 2번 스냅샷
	for (int i = 0; i < 20; i++)
	{
		Item* item = inventory[i];
		if (item == nullptr)
			continue;

		delete item;
	}
        // crt 메모리 체크 : 메모리 Leak이 발생할 시 출력창에 표기 됨
	_CrtDumpMemoryLeaks();

        // 3번 스냅샷
	return 0;  
}
  1. virtual을 사용한 경우
  2. virtual을 사용하지 않은 경우

답변 2

1

igbtkd님의 프로필 이미지
igbtkd
질문자

아! 제가 여쭙고 싶었던 것은 Item의 크기와 Weapon의 크기가 다를 때 delete item을 했을 때 Weapon 내부의 데이터(int와 같은 고정크기 데이터 포함)를 모두 지울 수 있는 지의 여부였습니다.  확인해보니 Weapon 생성 당시에 크기를 미리 메모리에 저장해두어서 소멸자 호출 여부와 관계없이 데이터를 지우는 것을 보았습니다! :) virtual 소멸자는 이러한 용도가 아니라 각각 자식 요소의 동적 할당에 대한 힙 제거 용이었네요! 감사합니다! 

1

Rookiss님의 프로필 이미지
Rookiss
지식공유자

위의 예제에서는 Item 소멸자에서
딱히 뭔가를 정리하고 있지는 않다보니,
설령 Item 소멸자가 호출되지 않더라도 다른 부분이 없습니다.

테스트를 위해선 Item 소멸자에서 '유의미한' 정리를 해주면 되는데요.
가령 char _dummy[4096]과 같은 고정크기 배열 대신 char* _dummy;
로 만들고 Item 생성자에서 = new char[4096]로 동적 할당을,
소멸자에서 delete[] _dummy로 삭제하는 코드 넣고
다시 테스트를 해보면 Leak을 확인할 수 있을겁니다.

안녕하세요 강사님 위와 같은 코드 각 Armor와 Weapon 클래스 소멸자에 virtual 함수가 없는데요 저럴 경우에도 메모리 누설이 일어나지 않는다고 하셨는데요, 그러면 굳이 virtual 을 안쓰고 저 코드 그대로 써도 메모리 누설이 발생하지 않으면 그대로 써도 되는 거아닌가? 라는 의문점이 들었습니다. 

igbtkd님의 프로필 이미지
igbtkd

작성한 질문수

질문하기