C++ 이름공간(Namespace)과 using

프로그래밍/C++ 2019.09.10 댓글 Plorence

C++ 표준은 이름 사용 범위를 더 잘 제어할 수 있도록 '이름공간'이라는 기능을 제공합니다.
최신의 이름공간을 배우기 전에, C++가 이미 가지고 있었던 이름 공간 기능을 간단하게 설명해보겠습니다.

 

구식 이름 공간

선언 영역(declarative region)

선언을 할 수 있는 영역입니다. 전역 변수의 선언 영역은 선언된 파일이고 지역변수의 선언 영역은 선언된 블록입니다.

선언 영역을 그림으로 나타냄

 

잠재 사용 범위(potential scope)

선언한 지점부터 선언 영역의 끝까지를 잠재 사용 범위라고 말합니다.
잠재 사용 범위는 선언 영역에 비해 좁습니다.

잠재 사용 범위을 그림으로 나타냄

말 그대로 잠재 사용 범위는 해당 변수의 사용이 가능한 범위를 말합니다.
사용 범위는 해당 변수에 접근(읽기/쓰기)을 한 범위입니다.

이것은 지역변수에 대해 공부를 하셨으면 쉽게 이해가 가능한 용어입니다.

 

최신 C++에서의 이름공간

새로운 종류의 선언 영역을 정의함으로써 이름이 명명된 이름 공간을 만들 수 있는 기능이 C++에 새로 추가되었습니다. 

목적은 이름을 선언하는 영역을 따로 제공하는 것입니다.
하나의 이름공간에 속한 이름은 다른 이름공간에 속한 이름과 충돌하지 않습니다.
사용하려면 namespace라는 키워드를 사용하면 됩니다.

 

이름공간의 필요성

이름 공간이 왜 필요한가에 대해 의문점을 가질 수 있습니다.
먼저 예시를 하나 들어보겠습니다.
구글사의 라이브러리, 애플사의 라이브러리를 사용하는데 둘 다 똑같은 이름을 가지고 있는 함수가 있다고 가정합시다. 

이 함수의 이름은 Display입니다.
이름 공간이 나오기 전 C++은 충돌 문제가 생길 것입니다.
하지만 이름 공간이 나오고 나서는 이 문제가 해결되었습니다.

#include <iostream>
namespace Google {
    void Display();
}
namespace Apple {
    void Display();
}
int main(void) {
    Google::Display();
    Apple::Display();
}

이름 공간 내부에 접근하려면 ::연산자(범위 확인 연산자라고 부름.)를 사용하시면 됩니다.
Google::Display();처럼 이름 공간이 지정된 이름을 제한된 이름(qualified name)이라고 합니다.

 

using

C++ 이름 공간을 보다 간편하게 사용할 수 있도록 두 가지 방법을 제공합니다.

  1. 선언
  2. 지시자

선언은 하나의 특별한 식별자를 사용할 수 있게 만듭니다.
지시자는 그 이름 공간 전체에 접근할 수 있게 만듭니다.

 

선언

namespace Google {
    void Display();
}
using Google::Display;

제한된 이름 앞에 using을 붙여주면 됩니다.
using 선언을 하게 되면 ::연산자로 접근을 하지 않아도 Display();만 써도 접근이 가능하게 됩니다.
마치 우리가

 using namespace std;

하는 거랑 동일한 효과입니다.
안 써주면

std::cout << "Hello World"; 

이렇게 써야 합니다.
단점은 using 선언을 하게 되면 동일한 이름으로 선언을 못합니다.

#include <iostream>
namespace Google {
    int num = 0;
}
int main(void) {
    using Google::num;
    int num = -1;
    printf("%d", num);
}

VS(Visual Studio) 기준으로 재정의를 했다고 컴파일 에러가 발생합니다.
using은 변수와 마찬가지로 선언 영역에서만 그 효과가 발생합니다.

#include <iostream>
namespace Google {
    int num = 0;
}
int num = -1;
int main(void) {
    using Google::num;
    printf("%d",::num);
}

using Google::num 때문에 전역 변수 num이 가려지게 되고 num의 대상은 Google이라는 이름공간 안에 있는 num이 됩니다.
전역에 있는 변수(num)에 접근하고 싶다면 ::연산자를 사용하면 됩니다.

 

지시자

지시자는 해당 이름공간에 속한 모든 이름을 ::연산자를 사용하지 않고도 가능케합니다.
사용방법은 선언과 비슷하면서도 간단합니다.
선언과의 차이점은 "하나의 이름"이고 지시자는 "하나의 이름공간"입니다.

using namespace Google;

선언과는 다르게 using뒤에 namespace라는 키워드가 붙었습니다.

#include <iostream>
namespace Google {
    int num = 0;
    double num2 = 123.11;
}
int main(void) {
    using namespace Google;
    printf("%d",num);
    printf("%f", num2);
}

이처럼 지시자는 해당 이름공간의 모든 이름을 ::연산자 없이 사용이 가능합니다.

이름공간은 중첩도 가능하다.

namespace Google {
    namespace A {
        int a = 0;
    }
    int num = 0;
    double num2 = 123.11;
}

이름공간 내에 또 다른 이름공간이 존재할 수 있으며 접근도 마찬가지로 ::연산자로 접근합니다.

 

이름공간에 이름이 없을 때

#include <iostream>
namespace {
    namespace A {
        int a = 0;
    }
    int num = 0;
    double num2 = 123.11;
}
int main(void) {
    
    printf("%d",num);
    printf("%f", num2);
}

이름공간의 이름이 없을 수도 있습니다.
이 경우에는 using이 있는 것처럼 동작하게 됩니다. 즉,::연산자로 접근을 안 해도 됩니다.
이름공간은 선언한 파일 외에서 사용이 불가능합니다.

댓글