클래스 전방 선언(Forward declarations)와 상호 참조 헤더 파일

Linux/C++ 2013. 12. 4. 10:57

class A 가 a.h에 있고

class B 가 b.h 에 있는데

B가 A를 사용하고 A가 B를 사용할 경우 :

거의 설계가 잘 못되었다고 볼 수 있다.

이렇게 프로그램을 짜면 이해하기가 힘들고 추적이 힘들기 때문에 경험많은

프로그래머들은 이렇게 짜지를 않는다.

 

흔히 전방선언이라고 하는 것은

 

class A;

class B

{

...

    A *pA;

};

 

와 같이 포인터만 이용할 때 가능합니다. class A의 포인터라면 그냥 4바이트(32bit OS)만

할당해 놓을 수 있지만 이것을

 

class A;

class B

{

...

   A a;

};

 

와 같이 하면 몇 byte를 할당해야 할지 알 수가 없으므로 에러를 냅니다.

이럴 경우에는 할 수 없이

 

#include "a.h"

class B

{

...

    A a;

};

 

이렇게 해야하는데

A a;

를 확인하려고 a.h 를 살펴보니

 

#include "b.h"

class A

{

...

   B b;

};

 

이렇게 (헤더 꼬임 현상)이 발생해서 무한 루프에 빠지게 됩니다.

(당연히 최상단에 올린 해더는 아래 해더를 모르므로 컴파일러는 알아서 에러를 내줍니다.)

이를 해결 하는 방법은 포함순서를 잘 맞추든지 아니면...

설계를 약간 바꾸는 것(전방 선언후 객체의 포인터 사용)이 좋지 않을까 합니다.

 

//////////////

 

class A / class B 가 있다. 그리고 A 는  B 를 사용할 것이다.

그럼 우리는 무엇을 해주어야 하는가?

 물론 A 쪽에 #include "B.h" 를 해주어야 할 것이다. 그래야 A 에서 B를 사용할 수 있으니까....

 

그런데.......

#include 의 수가 많아질수록 컴파일 속도가 저하된다는 것도 아시는지.....

이유는 생각해보면 알수 있다. 위에서 처럼 A 에 B 를 include 한다고 할때 만약 B.h 가 수정되면 include 부분도 이 영향을 받게 되며 수정이 많아질수록 컴파일 속도도 느려지게 된다.

 

이것을 해결하는 방법은? 전방선언을 사용하는 것이다.

(전방선언의 좋은점은 참조하려는 해더파일에 변경이 생겨도 참조 하는 해더파일에서는

재컴파일이 이루어 지지 않는다는 점이다.)

 

전방선언이란 가령 Player 라는 클래스가 선언된 헤더파일이 있을 경우

#include "Player.h" 대신

class Player;

이렇게 선언하는 것이다.

 

단, 주의해야 할 점이 있다. 전방선언자를 사용할 경우에는 그 클래스 관련 객체는 포인터형으로 선언해야 한다는 것이다. 만약 포인터형이 아닌 객체를 생성할 경우 전방선언자의 특징상 그 객체의 크기를 정확히 파악하여 할당을 못해주기 때문이다. (해당 클래스가 있다는 정보만 알고 사이즈를 모른다)

 

컴파일 적인 면에서 우리는 전방선언이 인클루드보다 속도, 의존관계 면에서 더 좋다고 하였다.

여기에 하나 더 추가하자면 다음과 같은 경우 우리는 전방선언을 유용하게 써먹을 수 있다.

 

class A, B 가 있다.

B는 A를 사용하고 있다 ( A 헤더를 인클루드하여 사용.... 상속은 아니다 )

그런데 우리는 부득이하게 A에서 B의 정보를 알아야 하는 상황에 놓이게 된다면....?

 

A는 B의 데이터를 알기위해선 B 의 구조를 알아야 하고 즉 B 헤더를 인클루드 해야 된다.

하지만 B에서 이미 A 헤더를 인클루드 하고 있기에 A 에서 B 헤더를 인클루드하면 상호 참조 에러가 되어버리고 만다. 즉 구조를 바꾸지 않는한 A 는 B 헤더를 인클루드 할수 없다는 것이다.

 

만약에 인클루드 대신에 앞에글에서 처럼 전방선언자를 사용한다면...?

 

#include "A.h"                                class B;

 

class B                                         class A

{                                                  {

   ///....                                           ///....

}                                                   }

 

이렇게 하면 우선 에러는 나지 않는다. ( 상호참조의 초석을 놓았다 )

다만 전방선언자를 사용하는 class A 에서는 B 를 포인터형으로 선언 또는 받는 처리만 가능할뿐

이를 동적 생성하거나 함수를 호출하면 에러가 나게 된다. 왜냐하면 전방선언자는 단순히 선언이기 때문에 생성, 호출은 실제 데이터 구조를 모르는 상태이므로 에러가 나게 되는 것이다.

그럼 동적 생성이나 호출을 하고 싶으면 어떻게 해야 하는가?

A.cpp 에서 구현을 하면 된다. 그리고 cpp 에 B.h 를 인클루드 하면 비로소 상호 참조가 가능하게 된다.


[출처] [펌] 전방선언자 (게임 프로그래밍 카페) |작성자 나르메


'Linux > C++' 카테고리의 다른 글

가상함수  (0) 2013.12.04