C언어에는 없던 이름공간(namespace)!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #include <iostream> namespace BestComImpl { void SimpleFunc(void) { std::cout << "BestCom이 정의한 함수" << std::endl; } } namespace ProgComImpl{ void SimpleFunc(void) { std::cout << "ProgComImpl이 정의한 함수" << std::endl; } } int main() { BestComImpl::SimpleFunc(); ProgComImpl::SimpleFunc(); return 0; } | cs |
이름공간(namespace)
위의 예제를 통해 (이름공간)namespace의 기본 개념부터 알아보겠습니다.
위의 에제에서 namespace영역이 정의 되어있지 않고 void SimpleFunc(void){}라는 함수들로만 선언되어있다고 생각하죠. 앞서 살펴본대로 C++는 함수의 오버로딩을 지원하지만 매개변수의 선언이 다른 경우에만 지원한다고 하였습니다. 따라서 함수의 이름과 매개변수의 이름이 같아서 오류가 발생할 것입니다.
"누가 이렇게 코드를 짜나요"라고 생각하실 수 있겠지만 여러사람이 협업하는 경우에는 일어날 수 있는 일입니다. 이를 해결하기 위해서 나온 것이 이름공간(namespace)의 개념입니다.
이름공간을 만드는 법은 간단합니다. 위의 예제처럼 정의할 함수를 namespace '이름공간'으로 덮어주면 되는 것이죠.
그리고 함수를 호출할때에는 '이름공간'::'함수의 이름';으로 호출해 주시면 되겠습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | #include <iostream> namespace BestComImpl { void SimpleFunc(void); } namespace BestComImpl { void PrettyFunc(void); } namespace ProgComImpl { void SimpleFunc(void); } int main() { BestComImpl::SimpleFunc(); return 0; } void BestComImpl::SimpleFunc(void) { std::cout << "BestCom이 정의한 함수" << std::endl; PrettyFunc(); ProgComImpl::SimpleFunc; } void BestComImpl::PrettyFunc(void) { std::cout << "So Pretty!!" << std::endl; } void ProgComImpl::SimpleFunc(void) { std::cout << "ProgCom이 정의한 함수" << std::endl; } |
다음 확장된 예제를 통해서 몇가지 내용을 더 알아보겠습니다.
1. 먼저 함수의 선언과 정의를 구분하는 방법입니다. 3행 ~ 13행처럼 이름공간 안에 함수의 선언만 삽입하면 됩니다.
예제에서는 동일한 이름공간은 BestComInpl을 두번 사용하여 SimpleFunc(void)와 PrettyFunc(void)를 따로 선언하였지만 동시에 선언하는 것도 가능합니다.
2. 동일한 이름공간에 정의된 함수를 호출할 때에는 이름공간을 명시할 필요가 없습니다.
22행을 보면 이름공간 BestComImpl 안에 선언된 SimpleFunc(void)의 정의하면서 동일한 이름공간안에 정의된 함수인 PrettyFunc();를 호출할때에는 이름 공간을 명시할 필요가 없음을 알 수 있습니다.
여기서 새로운 의문이 생깁니다. ::는 무엇을 의미하는 것일까?
:: 범위지정 연산자
이번 이름공간을 이야기 할때 뿐아니라 전에 입출력 부분에서도 ::를 자주 보셨을 것입니다. ::은 범위지정 연산자이며 이름 그대로 이름공간을 지정할 때 사용하는 연산자 입니다. 예를 들어 16행을 보면 BestComImpl::SimpleFunc();의 의미는 BestComImpl이라는 이름공간 안에있는 SimpleFunc()라는 함수를 호출해라의 의미인 것입니다.
그럼 이제 유추할 수 있습니다. 지금까지 봐왔던 입출력함수들 std::cout, std::cin, std::endl의 의미는 각각 "이름공간 std에 선언된 cout, cin, endl을 호출하라"는 의미였던것이죠.
범위지정 연산자의 또 다른 사용법도 있습니다.
int val = 100; //전역변수
int SimpleFunc(void) {
int val = 20; //지역변수
val += 3; //지역변수 val의 값 3 증가
::val += 7; //전역변수 val의 값 7 증가
}
위의 예제처럼 전역변수와 지역변수의 이름이 같을 경우 범위지정연산자를 사용하여 전역변수에 접근할 수 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #include <iostream> using std::cin; using std::cout; using std::endl; int main(void) { int num = 20; cout << "Hello World!" << endl; cout << "Hello " << "World!" << endl; cout << num << ' ' << 'A'; cout << ' ' << 3.14 << endl; return 0; } |
using을 이용한 이름공간의 명시
지금까지 입출력함수를 호출할 때마다 std::을 앞에 붙여야하는 과정이 너무 귀찮았다면 using을 이용한 선언을 통해 해결할 수 있습니다. 위의 예제에서 3행 ~ 6행이 의미하는 것은 앞으로 cin, cout, endl을 사용할 때 이름공간을 지정하지 않겠다는 것입니다.
이것마저 귀찮다면 다음과 같이 줄일 수 있습니다. using namespace std; 이것이 의미하는 것은 예상하셨다시피 이름공간 std에 선언된 모든 것에 대해 이름공간 지정을 생략하겠다라는 것입니다.
이 방법이 매우 매력적이고 편하겠지만 남용에 있어서는 주의해야합니다. 생략하는만큼 이름충돌이 발생활 확률이 높아지기 때문입니다.
이름공간의 별칭 지정
이름공간이 중첩되면서 까지 사용되는 경우는 극히 드물지만 과도하게 중첩되어있다고 가정합시다. AAA라는 이름공간안에 이름공간 BBB가 있고 그 안에 이름공간 CCC가있고 그안에 변수 num이 있다고 가정합시다. num을 호출하기위해 사용자는 AAA::BBB::CCC::num = 10이런 식으로 접근해야합니다. 너무 불편하기 때문에 새로운 방법을 고안한 것이죠. namespace ABC = AAA::BBB::CCC; 라는 선언문을 통해서 별칭을 지정해줄 수 있습니다. 이 한줄을 통해서 앞으로는 ABC::num = 10;의 형태로 접근할 수 있는 것입니다.
정리
1. 이름공간(namespace)을 이용하여 함수가 중복되는 경우를 해결할 수 있다.
2. 범위지정 연산자는 이름공간으 지정할 떄 사용하는 연산자이다.
3. using을 이용하여 이름공간의 지정을 생략할 수 있다.
출저: 열혈 C++ 프로그래밍 - 저자 윤성우