본문 바로가기

프로그래밍/C++

My C++ Tips

인클루드 순서

인클루드 순서야 정해진 룰은 없습니다.
그래도 제가 지켜서 쓰는 방식이 있는데요,
바로

미리 컴파일된 헤더에 해당하는 부분을 먼저 인클루드.

하는 겁니다.

즉, 좀 더 구체적이고(비추상적이고) 대중적인(?) 헤더를 먼저 인클루드 하는 거죠.
예를들면..

Win32 Library -> C RunTime Library -> STL Library -> Other Library -> My Library -> My Header

이런 식입니다.
(아, 참고로 이 인클루드 순서는 CPP 기준입니다. 미리 컴파일된 헤더는 CPP 기준이니까요.)
무엇이 일반적이고 대중적이냐를 판단하는 가늠은 여러가지이지만..
Win32 Library는 Windows라는 운영체제 API로 OS-Dependant 하므로 제일 먼저 위치합니다.
게다가 Winsock2.h 처럼 무조건 Windows.h 위에 와야되는 헤더도 있구요.
여러가지 귀찮은 것들이 많기 때문에 위로 올립니다.
제일먼저 미리 컴파일된 헤더가 되는게 여러모로 좋은 것들이죠.

그리고 C 라이브러리와 STL.
C 라이브러리가 좀 더 하위층의 개념이니깐
먼저 위치합니다.

Other Library는 그 외 내가 만들지 않은 알려진 라이브러리들이죠.
boost라든지 Loki라든지.. 있겠죠.

그 다음에 내가 만든 Library가 위치합니다.
마지막으로 내 cpp에 맞는 헤더 파일이 위치하겠죠.

미리 컴파일된 헤더는 해당 헤더의 내용을 미리 컴파일 해 놓겠다는 뜻이니
헤더의 내용이 잘 바뀌지 않을 내용이 들어가야 하겠죠.
Win32 Library야 운영체제를 바꾸지 않은 한 바뀔일이 없을거고
(운영체제를 바꾸게되면 미리 컴파일된 헤더도 마찬가지로 다시 만들어야 하겠죠.)
내가 만든 라이브러리는 꽤나 수시로 변할테니 가장 나중에 인클루드 합니다.
다른 사람이 만든 라이브러리일 경우에도
내가 수정할 가능성이 있는 경우에는 좀 더 늦게 인클루드 하는 게 좋겠죠.
각 항목 내부에도 변경될 가능성이 큰 것들을 나중에 인클루드 하는 겁니다.

예를들어,
현재 내가 작성하는 cpp가 App.cpp라면

#include <windows.h> // Win32 Library
#include <stdio.h> // C RunTime Library
#include <fstream> // STL Library
#include <boost/const.h> // Other Library
#include <MyLibrary/String.h> // My Library
#include "App.h" // My Header
이런 식으로 인클루드를 거는거죠.

미리 컴파일된 헤더를 만들지 않는다면 무슨 의미가 있습니까? 하고 물으신다면.
의미 없습니다.
뭐 그저 편의일 뿐입니다만,
저렇게 해놓는 버릇을 들인다면
자주 수정될 라이브러리와 안정적인 라이브러리를 헤더만으로 파악하기 쉬울 겁니다.
혹시나 프로젝트가 안정적으로 되어 미리 컴파일된 헤더를 작성할 때도
위에서부터 빼서 작성하면 되니 좀 더 편하겠지요.

제목에도 적었듯이 저의 C++ 팁입니다.
정해진 건 아니에요.


글로벌(Global) 함수와 클래스 스태틱(Static) 함수의 차이

예제를 들어보면 이런 겁니다.
void foo(); // 글로벌 함수
class Bar {
    static void foo(); // 클래스 스태틱 함수
};

두 함수의 차이점이 뭘까요?

글로벌 함수는 글자수가 좀 더 적다?
그것도 맞는 말이죠. 근데 그러면 클래스를 쓸 필요가 없잖아요?
연필값, 아니 손가락 누르는 값이 덜 들잖아요.

그럼 클래스 스태틱 함수가 글로벌 함수에 비해 나은 점은 무엇일까요?

바로 선언되지 않은 함수의 정의를 할 수 없습니다.

할 수 없는게 뭐가 장점이냐?
장점입니다. 컴파일 타임에 실수를 잡아주거든요.

자 다음과 같은 상황을 봅시다.

// 글로벌 함수
void believe();
void beleive()
{
     // TODO ...
}

// 클래스 스태틱 함수
class Bar {
    static void believe();
};
void Bar::beleive()
{
    // TODO ...
}

글로벌 함수의 예제의 경우
컴파일러는 이런 식으로 인식합니다.
선언된 void believe() 함수와
정의된 void beleive() 함수.
실은 개인적인 오타였겠지만 (아닐수도 있지만!)
나중에 believe() 함수를 사용하려고 하면 정의가 없다고 나오겠죠.

아니, 난 밑에 정의를 해두었다고 !!!
라고는 하지만 그게 오타였다고 발견하기 까지는...
그리고 발견하고 나서의 허탈감은...
(뭐 제가 이런일을 겪어서 이런 글을 쓰고 있는건.. 맞습니다.)

beleive() 함수를 사용하도록 했다면 문제가 없겠지만..
보통 선언은 헤더에 몰아두고 인클루드를 하는 것이니
왠간해서는 정의가 없다고 링킹 에러가 뜨겠죠..

클래스 스태틱 함수의 예제의 경우는 어떨까요?
친절하게 컴파일러님께서
Bar 클래스엔 beleive()라는 함수 선언이 없는데 정의가 되어있네요.
라는 메시지를 뿌려줍니다.
클래스는 선언되지 않은 함수는 정의할 수 없다..
제약과 동시에 링킹 에러가 컴파일 타임으로 넘어왔네요 !
이정도 제약에 링킹 에러를 없앨 수 있다면 저는 당연히 이쪽을 택하겠습니다 !

아, 그럼 글로벌 함수는 불안하기만 하고 쓸 게 못되나요?
하신다면 꼭 그렇지만은 할 수 없습니다.
제약이 없다는 것은 그만큼 융통성이 있다는 거거든요.

[C++] 헤더파일을 나누어 은닉화 구현하기

이런게 가능합니다.
사용자 단위로 보여주고 싶은 만큼만 보여줄 수 있습니다.
이래저래 뚫릴 가능성이 있는 private 함수보다 더욱 강력한 보안 정책입니다.
클래스 함수로는 저런 것이 불가능하죠.
클래스 정의는 따로따로 정의할 수 없기 때문이죠.
그렇게 정의할 수 있다면 위의 상황을 컴파일 타임에서 잡아낼 수 없겠죠.

자신의 파일에서만 쓰는 함수를 cpp에만 위치하게 하는 것도 가능합니다.
이 경우 다른 사용자는 그 함수의 존재 자체를 모르게 되지요.
왜냐하면 보통 인클루드는 헤더만 하기 때문이죠.
(물론 존재를 알면 임의로 선언을 해서 사용해버릴 수 있다는 단점이 있기는 있습니다.)

어쨋든
글로벌 함수와 클래스 스태틱 함수는
에러 검출과 융통성 사이에서 적절히 가치판단을 해서 이용하면 되겠습니다.
그래서 저는,
외부에 노출이 필요한 것들은 클래스 스태틱 함수로
내부에서만 사용할 것들은 글로벌 함수로
이용하고 있습니다.

C++을 이용하는 사람들에게 전역(글로벌) 함수를 권하는 사람들은 잘 없지요.
다시 얘기하지만 이건 저만의 C++ 팁입니다.
C++은 여러 패러다임을 지닌 언어이고
굳이 한가지 패러다임에 종속될 필요는 없겠지요.

간만에 프로그래밍 글이네요.
최근엔 일이 바빠서 플래시는 못만지고 C++을 계속 다루다보니
또 C++ 관련글이 나와버렸네요. ㅋ.
얼른 플래시를 손대보았으면..

'프로그래밍 > C++' 카테고리의 다른 글

How to initialize a constant class array  (2) 2010.08.18
메시지 처리기  (0) 2009.10.08
STL에 대한 단상  (4) 2008.01.29
C++ 기본 문제 By Choo  (6) 2007.07.26
임시변수의 범위는 어디까지일까요  (2) 2007.07.13