본문 바로가기

프로그래밍/C++

[C++] 이름 공간(Name space) - 1

들어가며

오늘은 '이름 공간(Name space)'에 관하여 알아볼 생각이다.

말 그대로 공간에다가 이름을 갖다 붙인다는 것이다.

공간이라고 하니까 생각난 것인데, 보편적인 학생의 방을 떠올려 보자.

방에는 침대가 있고, 옷장이 있으며, 책상 등등의 가구가 배치되어 있다.

왜 많은 사람들은 집의 테이블이나 책상을 놔두고 굳이 독서실이나 스터디 카페를 찾을까?

왜 서제와 침실을 분리해 놓을까?

우린 어떤 행동을 할 때 최대의 효율과 집중력을 발휘하기를 바란다.

침대 옆에서, 컴퓨터 옆에서 공부하다가 정신 차려보면

드러누워서 잔다거나, 소환사의 협곡에서 정의를 지키고 있는

자신을 발견한 경험이 없잖아 있을 것이다.

이처럼 우린 다른 요소로부터 방해받지 않고 하고자 하는 행동을 하기 위해

역할마다 공간을 분리할 필요가 있다.

더욱 게으른 프로그래머가 되기 위해!

 

이름 공간 (Name space)의 등장 배경

프로그램이 대형화되어 가면서 함수 혹은 변수 등등 이름의 충돌 문제가 등장하였다.

협업을 한다고 가정하면 위와 같은 상황에 대해 충분히 납득할 수 있다.

"않이, 그럼 사전에 서로 협조해서 이름에 관한 규칙을 설정해 놓으면 되잖수?"

물논 그 방법도 나름의 해결 방법일 수도 있을 것이다.

하지만 그것은 한계가 분명하고 근본적으로 해결될 수 있을 것인가에 대해서는 의문이다.

그래서 C++의 표준에서는 '이름 공간(Name space)'이라는 문법을 정의해서 이러한 문제들에 관하여

근본적인 해결책을 제시하고 있다!

 

이름 공간의 기본 메커니즘

아래의 영상을 머리도 식힐 겸 한 번 보도록 하자.

정말 대환장 파티라는 말이 정말 어울리는 현장이다.

이처럼 동명이인(이름 충돌)이 있다면 다수의 상황에서 문제가 되리라는 것은 합리적으로 추론할 수 있다.

하지만 이 사람들의 주거지가 각자 다르다고 한다면 이들 중 한 명을 그저 "야, 김민수!"라고 부르는 것보다

"204호 연구실에 서식하는 대학원생 김민수!"라고 하면 특정한 한 명을 훨씬 잘 구분할 수 있는 것처럼

이름 공간도 이와 같은 원리를 따른다.

 

그럼 코드로 한 번 경험해 보도록 하자.

#include <iostream>

void simpleFunc()
{
    std::cout << "3층 열람실 공시생 김민수" << std::endl;
}

void simpleFunc()
{
    std::cout << "2층 열람실 취준생 김민수" << std::endl;
}

void main()
{
    simpleFunc();
    
    return 0;
}

별도의 설명이 없더라도 저 두 명의 김민수가 안타까운 것 더불어,

코드의 함수명, 매개변수형이 동일하기 때문에 문제가 됨을 알 수 있다.

그렇다면 이름 공간을 만들고 그 내부에 함수를 정의하거나 변수를 선언한다면?

namespace Floor_Sec       // 'Floor_Sec'이 이름공간의 이름이다. (2층)
{
	// 이름 공간의 내부
}

namespace Floor_Thr       // 'Floor_Thr'이 이름공간의 이름이다. (3층)
{
	// 이름 공간의 내부
}

 

열람실 관계자가 "김민수 씨, 민원이 너무 많으니까 퇴실 조치하겠습니다."라고 한다면 난감하지만,

"3층에 입실하신 김민수 씨, 민원이 많은 관계로 퇴실 조치하겠습니다."라고 하는 것이 합당한 것처럼,

이름 공간을 설정한다면 이름 충돌이 발생하는 것을 미연에 방지할 수 있다.

 

이름 공간의 간단한 사용 방법과 범위지정 연산자

사용 방법은 간단하다. 이름 공간을 선언한 상태에서,

어떤 이름 공간에서 어떤 함수를 사용할 것인지를 생각하고,

  • (이름 공간) :: (함수명)(전달 인자);

위의 양식에 맞추어 사용하면 된다.

자, 이렇게 이름 공간에 대하여 배웠으니 간단하게 코드를 통해 어떻게 사용하는 지를 알아보도록 하자.

#include <iostream>

namespace Floor_Thr
{
	void KimMinSu()
	{
	    std::cout << "3층 열람실 공시생 김민수" << std::endl;
	}
}

namespace Floor_Sec
{
	void KimMinSu()
	{
	    std::cout << "2층 열람실 취준생 김민수" << std::endl;
	}
}

void main()
{
    Floor_Thr::KimMinSu();	// 3층 열람실(이름 공간)에서 김민수 호명(KimMinSu함수 호출)
    Floor_Sec::KimMinSu();	// 2층 열람실(이름 공간)에서 김민수 호명(KimMinSu함수 호출)
    
    return 0;
}

예상대로 합리적이고 명시적이며 논리적으로 사용하는 모습이다.

그럼 이때까지 자주 보이던 '::'의 정체에 대해서 약간 의문을 가질 수 있다.

"얜 뭔데 여기저기서 튀어나오는 거냐!"

결론적으로는 '::'은 연산자이고, 이를 가리켜 '범위 지정 연산자(scope resolution operator)'라고 한다.

그 이름이 의미하는 것 처럼 이름 공간을 지정할 때 사용하는 연산자이다.

 

이름 공간 기반의 함수 선언과 정의의 구분

여자친구가 어제 다이어트를 한다고 선언한다.

"나 오늘부터 다이어트 시작할 거야!"

그리고 다음날 데이트 때, 카페에서 버블티를 시키고 점심으로는 피자에 샐러드바, 영화관에서 팝콘에 나쵸

간식으로 붕어빵과 크레이프, 저녁으로 치킨집에 가서 치맥을 거하게 해치웠다.

분명 어제까지만 해도 호기롭게 다이어트를 하겠다고 선전포고를 놓은 그녀였는데

나는 다이어트 한다고 하지 않았냐고 물었다.

"맛있게 먹으면 0칼로리야!"

이때까지 내가 다이어트의 정의를 잘 못 알고 있던 것일까.

아니면 급변하는 세상에서 다이어트의 정의가 언제 나 몰래 바뀐 것일까.

참고로 필자는 여자친구는 커녕 주변에 여자도 없다.

그런 내용의 만화가 있다면 추천 부탁한다.

아무든 위 사례처럼 함수는 선언과 정의를 분리하는 것이 일반적이다.

따라서 다음 예제를 통해서 이름 공간 기반의 함수 선언과 정의를 구분하는 방법을 설명하겠다.

#include <iostream>

namespace Floor_Sec                                // 이름 공간 내부에
{
    void KimMinSu();                               // 함수의 선언만 분리
}

namespace Floor_Thr
{
    void KimMinSu();
}

int main()
{
    Floor_Sec::KimMinSu();
    Floor_Thr::KimMinSu();

    return 0;
}

void Floor_Sec::KimMinSu()                          // 이름공간을 명시하고 ::연산자를 사용 후 함수명 표기
{                                                   // ::연산자는 함수 호출 외에도 다양하게 사용된다.
    std::cout << "2층 김민수 씨" << std::endl;      // 함수의 정의 분리
}

void Floor_Thr::KimMinSu()
{
    std::cout << "3층 김민수 씨" << std::endl;
}

위 코드에서 이름 공간 내부의 함수의 선언과 정의를 분리하는 방법을 알아 보았다.

 

참고로, 동일한 이름 공간에 정의된 함수를 호출할 때에는 이름 공간을 명시할 필요가 .

#include <iostream>

namespace Floor_Sec     // 이렇게 둘 이상의 영역으로 나뉘어서 선언하는 것도
{
	void KimMinSu();
}

namespace Floor_Sec     //  동일한 이름 공간으로 간주한다.
{
	void KimMinji();
}

namespace Floor_Thr
{
	void KimMinSu();
}

int main()
{
	Floor_Sec::KimMinSu();

	return 0;
}

void Floor_Sec::KimMinSu()
{
	std::cout << "2층 김민수 씨는" << std::endl;
	KimMinji();             // 동일한 이름 공간이므로 호출 시 이름 공간을 명시하지 않고 호출할 수 있다.
	Floor_Thr::KimMinSu();  // 함수 호출 위치가 어떻든 호출 방식에는 변함이 없다.
}

void Floor_Sec::KimMinji()
{
	std::cout << "2층의 김민지 씨와" << std::endl;
}

void Floor_Thr::KimMinSu()
{
	std::cout << "3층 김민수 씨와는 아무 관계가 없습니다." << std::endl;
}

이제 어렴풋이라도 이름 공간에 대한 특성이 무엇인지 이해하였을 것이다.

그럼에도 잘 모르겠다면... 필자의 부족이므로 다른 블로그들을 전전해보는 것도 좋은 방법이다.

 

마치며

어떤가? 이름 공간에 대하여 간단한 고찰을 해보았다.

이름 공간이 매우 유용하게 사용될 것 같아 보이므로 협업 시에 요긴하게 사용될 것 같다.

C++의 기본 문법을 익힐 때에는 간단한 예제를 직접 작성해 보면서 결과를 확인하는 것이 좋다.

또한 다른 코더들의 코드들을 직접 해석해 보며 실행 과정과 개념의 매커니즘 등을 숙지하는 것도 좋다.

그럼 다음 시간에 이름 공간에 대해 알아보도록 하자.


참고 및 출처

  • 윤성우 열혈 C++ 프로그래밍