34-1-나.네임 스페이스 작성 규칙

다음은 네임 스페이스와 네임 스페이스 내부에 명칭을 작성하는 규칙에 대해 알아보자. 지극히 상식적인 내용들이므로 한 번씩 읽어 보기만 하면 된다.

 

 네임 스페이스의 이름도 일종의 명칭이므로 다른 명칭과 중복되어서는 안된다. 다른 네임 스페이스와 구분되는 이름을 가져야 함은 물론이고 변수나 함수와도 같은 이름을 쓸 수 없다. 네임 스페이스가 명칭 충돌 문제를 해결하기 위한 장치인데 스스로의 이름이 중복될 수 있다는 재귀적인 문제가 있는 셈이다. 그러나 네임 스페이스는 한 프로그램에 많아야 한 두 개밖에 없으므로 다른 명칭보다는 충돌 가능성이 훨씬 더 작고 수정하기도 쉬운 편이다.

만약 외부 라이브러리와 네임 스페이스 이름이 중복된다면 변수나 타입이 중복되는 것과 같은 곤란한 상황이 될 것이다. 그래서 네임 스페이스의 이름은 가급적이면 길게 쓰고 또한 중복되지 않는 고유한 이름으로 작성해야 한다. 회사의 이름이나 홈페이지 주소, 개인 이메일 주소 등을 응용하여 네임 스페이스명을 작성하면 99.9999% 안전하다.

 네임 스페이스는 반드시 전역 영역에 선언해야 한다. 함수안에 선언할 수 없다는 뜻이며 다음과 같은 지역 네임 스페이스는 허가되지 않는다.

 

void func()

{

     namespace C {

          int z;

     }

     ....

 

불가능한 것은 아니겠지만 이런 문법을 제공할 필요가 없다고 해야 할 것이다. 지역변수는 함수 내부에만 알려지고 그 생명도 함수 내부로 국한되며 많아 봐야 몇 십개를 넘지 않으므로 이 함수 내에서만 고유한 이름을 가지면 된다. 게다가 함수 하나를 둘이서 만들지는 않으므로 충돌해 봤자 혼자 고치면 된다. 그래서 지역변수는 네임 스페이스 안에 넣을 필요도 없고 그런 문법도 허락되지 않는 것이다. 만약 지역변수간의 충돌이 너무 심해 함수를 유지하기 어려울 지경이라면 이 함수를 잘 못 만든 것이다. 함수는 너무 커서는 안되며 더 작은 기능 단위로 분할해야 한다.

네임 스페이스가 전역 영역에만 존재하기 때문에 네임 스페이스 내부에 선언되는 명칭들은 본질적으로 전역적이다. 주로 타입이나 함수 등 프로젝트 전반에 걸쳐 참조되어야 할 명칭들이 네임 스페이스에 선언된다.

 네임 스페이스끼리 중첩 가능하다. 즉, 네임 스페이스안에 또 다른 네임 스페이스를 선언할 수 있다는 얘기인데 중첩의 단계에 대한 제한은 없다. 네임 스페이스는 명칭들을 논리적으로 그룹화하는 역할을 하는데 그룹을 나누는 세부 단계가 필요하다면 여러 단계로 중첩할 수 있다.

 

namespace Game {

     namespace Graphic {

          struct Screen { };

     }

     namespace Sound {

          struct Sori { };

     }

}

 

Game 네임 스페이스 안에 Graphic, Sound 네임 스페이스가 있고 각 중첩 네임 스페이스안에 함수, 타입 등 필요한 명칭을 선언한 예이다. 이 상태에서 중첩된 명칭을 참조하려면 :: 연산자를 중첩 회수만큼 사용하여 Game::Graphic::Screen 식으로 쓴다. 중간 규모 정도의 프로젝트에서는 네임 스페이스를 중첩 시킬 경우가 별로 없으며 초거대 규모의 프로젝트에서나 드물게 사용된다. 수백명이 한 프로젝트를 개발한다면 팀별로, 개인별로 네임 스페이스를 만들어야 할 것이다.

 네임 스페이스는 항상 개방되어 있다. 그래서 같은 네임 스페이스를 여러 번 나누어 명칭을 선언할 수 있다. 꼭 한꺼번에 몰아서 네임 스페이스내의 모든 명칭을 일괄 선언해야 하는 것은 아니다.

 

namespace A {

     double i;

}

namespace B {

     int i;

}

namespace A {

     char name[32];

}

 

네임 스페이스 A가 두 번 선언되어 있는데 두 번째 선언에 의해 새로운 네임 스페이스 A가 만들어지는 것이 아니라 기존 A 영역에 새로운 명칭이 추가된다. 그래서 변수 i와 name은 둘 다 네임 스페이스 A의 소속으로 병합된다. 네임 스페이스가 개방되어 있기 때문에 여러 모듈에서 한 네임 스페이스에 필요한 명칭을 언제든지 선언할 수 있으며 여러 사람이 같은 네임 스페이스를 쓰는 것도 가능하다. 이런 개방성의 예는 앞서 클래스의 액세스 지정에서도 본 바 있다.

 네임 스페이스가 이름을 가지지 않을 수 있다. 키워드 namespace 다음에 { } 괄호를 바로 쓰고 괄호안에 명칭만 선언하면 된다.

 

namespace {

     int internal;

}

 

이렇게 되면 internal은 사실상 일반적인 전역변수와 동일하다고 볼 수 있으며 소속을 밝히 필요없이 internal이라는 명칭만으로 참조할 수 있다. 다만 이 선언이 있는 파일 내에서만 사용 가능하며 외부로 알려지지 않는다는 점만 다르다. 7-2-가 절에서 논한 외부 정적변수와 성격이 동일하다고 할 수 있는데 static은 C의 방식이고 익명 네임 스페이스는 C++의 방식이다.

 단일 모듈 프로젝트에서는 별 상관이 없지만 다중 모듈 프로젝트에서는 함수의 본체를 어디에 작성할 것인가 주의해야 한다. 여러 개의 모듈로 나누어진 프로젝트를 개발할 때는 보통 헤더 파일과 구현 파일을 따로 작성한다. 네임 스페이스안에 함수를 정의할 때 헤더 파일에 원형만 선언하고 구현 파일에 함수의 본체를 작성한다.

NsTest 프로젝트에 NsTest 메인 모듈과 이 모듈에 어떤 기능을 제공하는 Util 모듈이 있다고 하자. Util 모듈은 명칭 충돌 방지를 위해 네임 스페이스 안에 함수를 작성하고자 한다. 이때 함수의 원형 선언과 본체 정의는 다음과 같이 나누어져야 한다.

 

: NsTest

Util.h에는 네임 스페이스 A안에 func 함수의 원형이 기록되어 있다. 이 함수의 본체는 Util.cpp에 작성하되 본체 정의부의 함수명 앞에 소속 네임 스페이스인 A::을 반드시 적어야 한다. 아니면 네임 스페이스의 개방성을 활용하여 다음과 같이 할 수도 있다.

 

namespace A {

     void func()

     {

          printf("I am func\n");

     }

}

 

네임 스페이스가 반복되면 병합되므로 함수 선언과 본체 정의가 네임 스페이스 A에 합쳐질 것이다. 구현 파일을 따로 만들지 않고 헤더 파일에 함수의 본체를 바로 정의하는 것은 안된다. Util.cpp를 프로젝트에서 빼고 다음과 같이 코드를 수정해 보자.

일단은 컴파일되고 실행도 된다. 그러나 만약 제 3의 모듈에서 Util의 함수를 필요로 한다면 Util.h를 인클루드 할 때마다 func 함수의 본체가 따로 생성되므로 함수가 중복 정의되는 문제가 있다. 일반적으로 선언은 여러 번 반복할 수 있지만 정의는 단 한 번만 해야 한다.

이 점은 변수에 대해서도 마찬가지이다. 헤더 파일의 네임 스페이스안에 변수를 선언하면 이 헤더 파일을 인클루드하는 모든 모듈에 동일한 이름의 변수가 중복 생성될 것이다. 앞 부분에서 개념적인 이해의 편의를 위해 네임 스페이스에 포함되는 명칭으로 변수를 사용했지만 일반적으로 네임 스페이스에 변수나 함수 정의를 직접 하는 경우는 거의 없다. 주로 클래스나 구조체같은 타입 선언이 네임 스페이스에 배치된다. C++에서는 주로 클래스간의 충돌이 문제가 되며 변수나 함수는 클래스안에서 지역적이므로 문제가 되는 경우가 드물다.