전체 코드

1. 프로젝트 구조

이번 프로젝트에서는 전방 선언(Forward Declaration) 을 활용하여 객체 간의 의존성을 줄이고, 컴파일 속도를 최적화하였습니다.

📂 프로젝트 폴더 구조

📂 ForwardDeclarationExample
│── 📂 Creature
│    ├── Monster.h
│    ├── Monster.cpp
│── 📂 Player
│    ├── Player.h
│    ├── Player.cpp
│── main.cpp

2. 주요 개념

전방 선언(Forward Declaration)

  • 클래스가 다른 클래스를 포인터 또는 참조 형태로 포함할 때 사용
  • 컴파일 시간을 단축하고, 헤더 간 의존성을 줄임
  • 클래스의 멤버 변수로 다른 클래스를 직접 포함하면 #include 필요

헤더 추가 vs 전방 선언
| 방법 | 설명 | 장점 | 단점 |
|------|------|------|------|
| #include 사용 | 헤더 파일에서 직접 포함 | 클래스의 모든 내용을 사용 가능 | 불필요한 컴파일 시간 증가 |
| 전방 선언 (class Monster;) | 클래스의 선언만 제공 | 컴파일 속도 향상, 의존성 감소 | 클래스 내부 변수를 사용 불가 |


3. 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; 를 통해 p1p2를 가리키도록 설정.
  • delete p2; 로 동적 할당된 메모리를 해제하여 메모리 누수 방지.

4. 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으로 변경하여 몬스터를 "죽이는" 역할 수행.

5. 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으로 설정.

6. 전방 선언(Forward Declaration)의 필요성

🚫 #include 만 사용한 경우

#pragma once
#include "Monster.h" // 직접 포함

class Player
{
public:
    int _hp;
    int _attack;

    Monster _target; // Monster 클래스 전체를 포함 (메모리 낭비 발생)
};

문제점:

  • Player 클래스가 Monster의 크기를 알아야 하므로 Monster.h#include 해야 함.
  • Monster크기가 클 경우, 메모리 낭비 발생 가능.

✅ 전방 선언(Forward Declaration) 적용

#pragma once

class Monster; // 전방 선언

class Player
{
public:
    int _hp;
    int _attack;

    Monster* _target2;  // 포인터로만 선언하면 크기 정보를 몰라도 됨 (메모리 절약)
};

장점:

  • 컴파일 속도 향상
  • 헤더 파일 간의 의존성 감소
  • 불필요한 클래스 포함을 방지

7. 전방 선언이 필요한 경우

전방 선언 필요 여부예제설명
✅ 필요Monster* _target;포인터 또는 참조 타입이므로 크기 정보가 필요 없음
✅ 필요class Monster;포인터를 사용하므로 크기 정보 없이 선언 가능
❌ 필요 없음Monster _target;Monster 객체를 직접 포함하면 #include "Monster.h" 필수

8. 전방 선언을 사용하지 못하는 경우

🚫 불가능한 경우

#pragma once

class Monster;  // 전방 선언

class Player
{
public:
    int _hp;
    int _attack;

    Monster _target; // Monster의 전체 크기를 알아야 하므로 전방 선언 불가능!
};

해결 방법:

  • Monster포인터 또는 참조 타입으로 변경해야 전방 선언 가능.
  • #include "Monster.h" 를 추가하여 Monster의 크기를 알게 해야 함.

9. 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; ✅ → 포인터는 크기가 고정되므로 전방 선언 가능.

profile
李家네_공부방

0개의 댓글