이번 프로젝트에서는 전방 선언(Forward Declaration) 을 활용하여 객체 간의 의존성을 줄이고, 컴파일 속도를 최적화하였습니다.
📂 ForwardDeclarationExample
│── 📂 Creature
│ ├── Monster.h
│ ├── Monster.cpp
│── 📂 Player
│ ├── Player.h
│ ├── Player.cpp
│── main.cpp
✅ 전방 선언(Forward Declaration)
#include 필요✅ 헤더 추가 vs 전방 선언
| 방법 | 설명 | 장점 | 단점 |
|------|------|------|------|
| #include 사용 | 헤더 파일에서 직접 포함 | 클래스의 모든 내용을 사용 가능 | 불필요한 컴파일 시간 증가 |
| 전방 선언 (class Monster;) | 클래스의 선언만 제공 | 컴파일 속도 향상, 의존성 감소 | 클래스 내부 변수를 사용 불가 |
main.cpp (메인 파일)#include <iostream>
#include "Player.h"
using namespace std;
int main()
{
// Player의 크기는?
// int 2개 + sizeof(Player*) (32비트: 4바이트, 64비트: 8바이트)
Player p1; // 스택에 할당된 지역 변수
Player* p2 = new Player(); // 동적 할당 (힙 메모리)
p1._target3 = p2; // Player의 포인터 멤버를 통해 다른 Player 객체를 가리킴
delete p2; // 동적 할당된 메모리 해제
return 0;
}
Player 객체 p1은 스택(Stack) 에 생성됨.new Player(); 로 생성된 p2는 힙(Heap) 에 생성됨.p1._target3 = p2; 를 통해 p1이 p2를 가리키도록 설정.delete p2; 로 동적 할당된 메모리를 해제하여 메모리 누수 방지.Monster 클래스 (전방 선언 적용)Monster.h#pragma once
class Monster
{
public:
void KillMe();
public:
int _monsterId; // +0
int _hp; // +4
int _defence; // +8
};
Monster.cpp#include "Monster.h"
void Monster::KillMe()
{
_hp = 0; // 체력을 0으로 설정하여 몬스터를 제거
}
Monster 클래스는 _monsterId, _hp, _defence 멤버 변수를 가짐.KillMe() 함수는 _hp 값을 0으로 변경하여 몬스터를 "죽이는" 역할 수행.Player 클래스 (전방 선언 활용)Player.h#pragma once
// #include "Monster.h" // 필요할 경우 포함 가능
// 전방 선언 (Forward Declaration)
class Monster;
class Player
{
public:
void KillMonster();
void KillMonster2();
public:
int _hp;
int _attack;
Monster* _target2; // Monster를 포인터로 가리킴 (전방 선언 가능)
Player* _target3; // Player를 가리키는 포인터 (자기 자신도 전방 선언 가능)
};
Player.cpp#include "Player.h"
#include "Monster.h" // Monster의 멤버 변수를 사용할 경우 필요
void Player::KillMonster()
{
_target2->_hp = 0; // _target2가 가리키는 Monster 객체의 체력을 0으로 설정
_target2->KillMe(); // Monster 클래스의 KillMe() 호출
}
void Player::KillMonster2()
{
_target2->_hp = 0;
}
Monster* _target2; → 포인터로 선언하여 전방 선언이 가능Player* _target3; → 자기 자신의 포인터를 포함할 수 있음KillMonster()에서 _target2->_hp = 0; 를 통해 몬스터의 체력을 0으로 설정.#include 만 사용한 경우#pragma once
#include "Monster.h" // 직접 포함
class Player
{
public:
int _hp;
int _attack;
Monster _target; // Monster 클래스 전체를 포함 (메모리 낭비 발생)
};
✅ 문제점:
Player 클래스가 Monster의 크기를 알아야 하므로 Monster.h를 #include 해야 함.Monster가 크기가 클 경우, 메모리 낭비 발생 가능.#pragma once
class Monster; // 전방 선언
class Player
{
public:
int _hp;
int _attack;
Monster* _target2; // 포인터로만 선언하면 크기 정보를 몰라도 됨 (메모리 절약)
};
✅ 장점:
| 전방 선언 필요 여부 | 예제 | 설명 |
|---|---|---|
| ✅ 필요 | Monster* _target; | 포인터 또는 참조 타입이므로 크기 정보가 필요 없음 |
| ✅ 필요 | class Monster; | 포인터를 사용하므로 크기 정보 없이 선언 가능 |
| ❌ 필요 없음 | Monster _target; | Monster 객체를 직접 포함하면 #include "Monster.h" 필수 |
#pragma once
class Monster; // 전방 선언
class Player
{
public:
int _hp;
int _attack;
Monster _target; // Monster의 전체 크기를 알아야 하므로 전방 선언 불가능!
};
✅ 해결 방법:
Monster를 포인터 또는 참조 타입으로 변경해야 전방 선언 가능.#include "Monster.h" 를 추가하여 Monster의 크기를 알게 해야 함.Player 클래스 내부에서 자기 자신을 포함할 경우#pragma once
class Player
{
public:
int _hp;
int _attack;
Player _target; // ❌ 오류 발생 (자기 자신의 크기를 알 수 없음)
};
✅ 해결 방법
#pragma once
class Player
{
public:
int _hp;
int _attack;
Player* _target; // ✅ 가능 (포인터 크기는 4 or 8 바이트로 고정)
};
Player _target; ❌ → 자기 자신의 크기를 알아야 하지만, 정의되지 않아 오류 발생.Player* _target; ✅ → 포인터는 크기가 고정되므로 전방 선언 가능.