C++ namespace
목차
이 포스팅에서는 C++ namespace(이름공간)에 대해서 알아보겠습니다.
이름공간에 대하여 너무 구애받을 필요는 없습니다. C++에서 이름공간이란 개념은 지역변수, 전역변수, extern 변수, 객체 등과 섞여서 흐릿한 구석이 있습니다.
컴파일러가 식별자(identifier – 변수, 함수 등)를 더 잘 구분하여 오류를 최소화하기 위한 기능입니다. 대형 프로젝트로 갈수록 많은 사람들이 변수를 만들기 때문에 충돌을 방지하기 위한 측면도 있습니다.
이름공간이란 아이디어는 C#, Python 등에도 이어지기 때문에 좀 일반적인 센스로 받아들일 필요가 있습니다.
이름공간이란 생소한 단어입니다. 이름공간에 대하여 인사이트를 갖기 위해서는 { } 괄호안에서만 사용되는 지역변수와 이 바깥에서 전역변수에 대하여 알아야 합니다.
지역변수와 전역변수
변수의 범위에서 다룰 내용이지만 잠깐만 보고 가겠습니다.
지역변수는 아래와 같은 변수를 말합니다. int var1은 main 함수안에 있는 지역변수 있니다.
int main() { int var1; return 0; }
규칙을 명확하게 알고 싶으면 아래와 같이 코드를 작성해봅니다. 정수형 var1 이라고 식별자가 다 같은데 이들은 각 지역에 포함된 다른 변수들입니다.
프로그래밍을 하면서 주의할 점은 ‘보이는 그대로 믿지 말라’는 것입니다. var1 과 var1 은 위치에 따라 다른 변수입니다.
int var1; int main() { int var1; { int var1; { int var1; } } return 0; }
확인을 해보겠습니다. main() 바깥에 있는 것은 전역변수고 나머지는 지역변수입니다.
지역변수의 초기값을 다르게 만들어서 각 지역에서 출력해봤습니다. 어떻게 될까요?
using std::cout; using std::endl; int var1 = 0; int main() { cout << var1 << endl; int var1 = 1; cout << var1 << endl; { int var1 = 2; cout << var1 << endl; { int var1 = 3; cout << var1 << endl; } } cout << var1 << endl; cout << ::var1 << endl; return 0; }
0 1 2 3 1 0
이 코드를 따라가보면 컴파일러가 어떻게 지역을 나눴는지 볼 수 있습니다. 또 컴파일러의 허점도 알게 됩니다. 마지막 줄에 ::var1 이 의미하는 것은 전역변수의 var1을 의미합니다. :: 이것은 이름공간을 지정하기 위한 연산자입니다. 이것이 없을 때는 현재의 지역에서 식별자(identifier)를 찾습니다.
namespace 이름공간 생성
이름공간은 외부라이브러리에서 주로 가져다가 쓰지만 사용자가 정의하는 것도 가능합니다.
namespace 키워드를 사용해서 지정할 수 있습니다. 이 안에 뭘 넣어도 이름공간:: 의 형식으로 꺼내올 수 있습니다.
아래의 예제 코드는 이름공간을 사용하여 컴파일러에게 어떤 영역에 접근할 것인지 알려주고 있습니다. 결과값을 볼 때 출력 내용과 순서를 보면 알 수 있습니다.
#include <iostream> using std::cout; using std::endl; namespace mySpace1 { int var1 = 111; void sayHello() { cout << "Hello mySpace1" << endl; } } int var1 = 999; void sayHello(){ cout << "Hello Global space" << endl; } int main() { int var1 = 777; sayHello(); mySpace1::sayHello(); cout << var1 << endl; cout << ::var1 << endl; cout << mySpace1::var1 << endl; return 0; }
Hello Global space Hello mySpace1 777 999 111
using namespace
using namespace 의 사용법을 알아봅니다.
#include <iostream> using std::cout; using std::endl; namespace mySpace1 { int var1 = 111; void sayHello() { cout << "Hello mySpace1" << endl; } } using namespace mySpace1; int main() { sayHello(); mySpace1::sayHello(); cout << var1 << endl; return 0; } [결과값] Hello mySpace1 Hello mySpace1 111
using namespace mySpace1을 하게 되면 mySpace1:: 을 붙이지 않고도 이름공간을 사용할 수 있습니다.
만약 지역 변수나 함수의 이름과 같다면 컴파일러는 모호하기 때문에 오류를 냅니다. 이름 공간의 각 변수와 함수의 이름이 겹치지 않도록 관리하는 것은 프로그래머의 몫입니다.
그리고 저위의 using std::cout 을 보면 이제 구조를 알 수 있습니다. using namespace std 는 일괄적으로 그 안에 들어있는 모든 이름을 사용한다는 것이고 cout 처럼 하나의 객체만 뽑아온다면 다른 것들은 건드리지 않고 이것만 사용하겠다고 골라온 것 입니다.
필요한 이름만 가져오는 것이 당연히 현명합니다. 여기서 퉁을 쳐서 using namespace std 를 해버리면 무슨 이름인지도 모르지만 지금 이름을 잡고 있는 겁니다. 물론 컴파일 시 겹치는 것은 확인이 가능하지만 사용하지 않을 라이브러리를 컴파일 단계에 끌어들이는 것은 별로 좋은 방법이 아닙니다.
실제 std 를 확인해보면 iostream, cmath 등 수십개 파일에서 수백개의 이름들을 포함하고 있습니다. 물론 이건 std(Standard) 표준파일이기 때문에 여기서 그닥 문제될 일은 없겠지만 namespace의 동작을 알게되면 찜찜하죠. 그냥 필요할 때 std::cout 처럼 사용하는게 좋습니다.
요약
약간 초반부에 커버하기는 뭐하지만 알고보면 별 내용이 아니기에 namespace 에 대하여 알아봤습니다.
중요한 것은 내가 소소코드를 쓰고 있는 라인이 어떤 영역이고 현재 영역에서 사용가능한 이름공간은 어떤 것이 있나 정도만 체크하면 됩니다.
외부참조
Namespaces – C++ Tutorials (cplusplus.com)
Namespaces in C++ – Tutorialspoint
Namespace in C++ | Set 1 (Introduction) – GeeksforGeeks