C++에서 미리 함수를 정의하지 않으면 순차적으로 코드를 읽어들여 오류를 발생시킨다. (식별자 찾을 수 없음)
그러나 전방선언이 이뤄졌다면 컴파일러는 오류 없이 지나간다.
# 22.11.22 추가
딱 아래와 같은 상황 때문에 클래스 전방선언이 필요하여 다시 찾아보게 되었다.
class A, B 가 있다.
B는 A를 사용하고 있다 ( A 헤더를 인클루드하여 사용.... 상속은 아니다 )
그런데 우리는 부득이하게 A에서 B의 정보를 알아야 하는 상황에 놓이게 된다면....?
A는 B의 데이터를 알기위해선 B 의 구조를 알아야 하고 즉 B 헤더를 인클루드 해야 된다.
하지만 B에서 이미 A 헤더를 인클루드 하고 있기에 A 에서 B 헤더를 인클루드하면 상호 참조 에러가 되어버리고 만다.
즉, 구조를 바꾸지 않는한 A 는 B 헤더를 인클루드 할수 없다는 것이다.
만약에 인클루드 대신에 처럼 전방선언자를 사용한다면...?
< B.h > < A.h >
#include "A.h" class B;
class B class A
{ {
///.... ///....
} }
이렇게 하면 우선 에러는 나지 않는다. ( 상호참조의 초석을 놓았다 )
다만 전방선언자를 사용하는 class A 에서는 B 를 포인터형으로 선언 또는 받는 처리만 가능할뿐
이를 동적 생성하거나 함수를 호출하면 에러가 나게 된다. 왜냐하면 전방선언자는 단순히 선언이기 때문에 생성, 호출은 실제 데이터 구조를 모르는 상태이므로 에러가 나게 되는 것이다.
그럼 동적 생성이나 호출을 하고 싶으면 어떻게 해야 하는가?
A.cpp 에서 구현을 하면 된다.
그리고 cpp 파일에서 B.h 를 인클루드 하면 비로소 상호 참조가 가능하게 된다.
-
// Archer.h
-
-
class Archer
-
{
-
// ...
-
};
-
// 전방선언
-
class Archer;
-
-
class RangeAttack
-
{
-
public:
-
// 포인터 형식으로 Archer 이름만 참조
-
RangeAttack(const Archer* archer);
-
// 참조 형식으로 Archer 이름만 참조
-
RangeAttack(const Archer& archer);
-
// 전방선언
-
class Archer;
-
-
class RangeAttack
-
{
-
public:
-
void SetArcher(Archer archer);
-
Archer GetArcher() const;
C++ 전방선언 (Forward Declaration)
식별자를 정의하기 전 식별자의 존재를 컴파일러에 미리 알리는 것
필요에 따라 함수, 변수, 클래스 등을 전방선언한다.
컴파일 시간을 단축시킨다.
헤더포함 의존성을 줄여준다.
int x; //선언
x = 42; //x를 사용
C++ 클래스 전방선언
헤더파일에서 헤더파일을 포함시키는 행위는 컴파일 시간을 증가시킨다.
(하나의 헤더파일이 변경되어도 include한 파일들이 모두 재컴파일)
A 에 B 를 include 한다고 할때 만약 B.h 가 수정되면 include 부분도 이 영향을 받게 되며 수정이 많아질수록 재컴파일 되기 때문에 컴파일 속도도 느려지게 된다.
// #include "ObjectA.h" 생략하고
class ObjectA; //클래스 전방선언
그래서 포인터 객체를 선언할 때 클래스 선언 전에 필요한 클래스를 명시해 헤더파일의 중복을 막아주는 것이다.
- C++ 클래스 전방선언은 헤더포함 의존성을 최소화
- 해당 헤더의 불필요한 정보 비노출
ObjectA *objA;
// ObjectA objA; //불가능
위 처럼 사용하는데 헤더파일을 포함하지 않으니 클래스에 대한 정보가 없어서
반드시 포인터 객체나 Ref 객체 형태만 사용 가능하다.
단, 전방 선언을 사용할 경우, 전방선언한 클래스에 대한 객체는 포인터형으로 선언해야 한다.
(
해당 객체 할당 크기를 정확히 파악할 수 없어서이며 포인터형으로 선언 시 4바이트(32bit OS에서)를 할당)
Mother* m_pMainFamily = nullptr;
C++ 함수 전방선언
함수의 몸체를 정의하기 전 함수의 존재를 컴파일러에 미리 알리는 것을 말한다.
함수 전방선언 방법은 함수 원형 (함수 선언문)을 사용하여 작성하면 된다.
그러면 리턴타입, 이름, 매개변수는 들어가지만 함수 몸체는 포함하지 않고 명령문이므로 세미콜론으로 끝난다.
#include <iostream>
int add(int x, int y); //전방선언
int main()
{
...
add(3, 4);
return 0;
}
int add(int x, int y) //정의
{
return x+y;
}
사용은 이런 식으로 한다. 함수 원형에서 매개변수의 이름을 생략해서 선언할 수도 있다.
int add(int, int); // 전방선언 이렇게도 가능
C++ 선언과 정의의 차이점
- 선언 (declaration) : 식별자(변수/함수이름) 및 해당타입의 존재를 컴파일러에 알려준다.
선언하지 않고 식별자를 사용하면 컴파일러에서 에러가 발생한다.
- 정의 (definition) : 식별자를 실제로 구현하거나 인스턴스화(메모리 할당) 한다.
정의하지 않고 식별자를 사용하면 링커에서 에러가 발생한다.
- C++에서 모든 정의는 선언으로도 간주한다. (int x;)
대부분은 정의만 필요하나 정의되기 전에 식별자를 사용해야 한다면 명시적인 선언이 필요하다.
C++ 매개변수 / 리턴 타입을 위한 이름만 참조하는 전방선언
함수의 매개변수나 리턴 타입으로 이름만 사용될 경우 포인터형이 아닌 객체를 사용할 수 있다.
함수 body 와 그 함수를 호출할 경우에만 클래스의 크기가 필요하기 때문이다.
// 전방선언
class Archer;
class RangeAttack
{
public:
void SetArcher(Archer archer);
Archer GetArcher() const;
출처: https://coding-restaurant.tistory.com/504 [코딩맛집:티스토리]
참조 : https://jrabbit.tistory.com/35
[c++] 클래스 전방 선언(Forward declarations)와 상호 참조 헤더 파일
class A 가 a.h에 있고class B 가 b.h 에 있는데B가 A를 사용하고 A가 B를 사용할 경우 :거의 설계가 잘 못되었다고 볼 수 있다.이렇게 프로그램을 짜면 이해하기가 힘들고 추적이 힘들기 때문에 경험많
jrabbit.tistory.com
http://egloos.zum.com/sweeper/v/2827565
[C++] 전방 선언 (Foward Declaration)
0. #include 의존성 최소화 큰 프로젝트를 컴파일하는 데 걸리는 시간은 #include를 얼마나 많이 그리고 얼마나 깊은 수준으로 사용하고 있느냐에 따라 달라진다. 일반적으로는 빌드 시간을 줄이기
egloos.zum.com
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;
이렇게 선언하는 것이다.
단, 주의해야 할 점이 있다. 전방선언자를 사용할 경우에는 그 클래스 관련 객체는 포인터형으로 선언해야 한다는 것이다. 만약 포인터형이 아닌 객체를 생성할 경우 전방선언자의 특징상 그 객체의 크기를 정확히 파악하여 할당을 못해주기 때문이다. (해당 클래스가 있다는 정보만 알고 사이즈를 모른다)
컴파일 적인 면에서 우리는 전방선언이 인클루드보다 속도, 의존관계 면에서 더 좋다고 하였다.
'Language & etc > C++' 카테고리의 다른 글
[컴파일/링킹]에서 본 Static Member Variables, function... (0) | 2022.07.02 |
---|---|
std::Move, perfect forwarding - ① (0) | 2022.06.19 |
LValue, RValue , 이동 생성자 깊게 이해하기 (feat.Copy Elision ) (0) | 2022.05.04 |
타입 변환 연산자 (0) | 2022.04.17 |
변환 생성자 (feat. explicit, delete) (0) | 2022.04.17 |