C++ Namespace 란?

C++ Namespace는 프로그램 내에서 식별자를 구별하는 데 사용되는 강력한 도구 중 하나입니다. 이것은 이름 충돌과 가독성 문제를 피하기 위해 개발자에게 유용한 방법을 제공합니다. 이제 C++ Namespace에 대해 자세히 알아보겠습니다.

C++ Namespace의 개념

동일한 이름을 가진 두 명의 학생이 같은 수업에 있다고 상상해보십시오. 그들을 구별하려면 이름 외에 추가 정보를 사용해야 합니다. 예를 들어, 서로 다른 지역에 살고 있는지 여부, 그들의 부모의 이름 등이 있습니다.

C++ 응용 프로그램에서도 동일한 상황이 발생할 수 있습니다. 예를 들어, hello()라는 함수를 포함한 코드를 작성 중이고 또 다른 라이브러리에도 동일한 함수 hello()가 있는 경우입니다. 이제 컴파일러는 코드 내에서 어떤 버전의 hello() 함수를 참조하고 있는지 알 수 없습니다.

이러한 어려움을 극복하기 위해 Namespace가 고안되었습니다. Namespace는 서로 다른 라이브러리에서 사용 가능한 유사한 함수, 클래스, 변수 및 동일한 이름을 구분하기 위한 추가 정보로 사용됩니다. Namespace를 사용하여 이름이 정의된 컨텍스트를 정의할 수 있습니다. Namespace는 범위를 정의합니다.

정의

Namespace는 원래 C에 없었던 C++에 추가된 요소입니다. Namespace는 식별자(종류, 함수, 변수 등의 이름)에 대한 범위를 제공하는 선언적 영역입니다. 동일한 이름을 가진 여러 Namespace 블록이 허용됩니다. 이러한 블록 내의 모든 선언은 해당 명명된 범위에서 선언됩니다.

C++의 Namespace는 애플리케이션을 관리하기 쉽게 여러 클래스를 구조화하는 데 사용됩니다. Namespace의 클래스에 액세스하려면 namespacename::classname을 사용해야 합니다. 우리는 항상 전체 이름을 사용할 필요가 없도록 키워드를 사용할 수 있습니다.

Namespace 정의는 다음과 같이 키워드 namespace 다음에 Namespace 이름으로 시작됩니다:

namespace namespace_name 
{
   int x, y; // x와 y는 namespace_name의 범위에서 선언됨
}

using 지시문

using 을 사용하면 네임스페이스 이름을 명시적 한정자로 사용하지 않고도 네임스페이스 내의 모든 이름을 사용할 수 있습니다. 네임스페이스 내에서 여러 식별자를 사용하는 경우 실행 파일 (예: *.cpp)에서 using 을 사용하십시오. 지역 변수가 네임스페이스 변수와 동일한 이름을 가지면 네임스페이스 변수는 숨겨집니다. 전역 변수와 동일한 이름을 가진 네임스페이스 변수를 가질 수 없습니다.

#include <iostream>

int main()
{
    std::cout << "hello";
    std::cout << "world";
    return 0;
}

이 문장을 다음과 같이 사용 할 수 있습니다.

#include <iostream>
using namespace std;

int main()
{
    cout << "hello";
    cout << "world";
    return 0;
}

네임스페이스 및 네임스페이스 멤버 선언:

보통 네임스페이스는 헤더 파일에서 선언합니다. 함수 구현이 별도의 파일에 있는 경우, 해당 예제처럼 함수 이름을 명시적으로 지정하십시오.

// harostudio.h
#pragma once
namespace harostudio
{
    void Foo();
    int Bar();
}

harostudio.cpp에서 함수를 구현할 때에도 파일 상단에 using 지시문을 두었다 해도 완전히 정규화된 이름을 사용해야 합니다.

// harostudio.cpp
#include "harostudio.h"
using namespace harostudio;

void harostudio::Foo() // 여기서 완전히 정규화된 이름 사용
{
    // Bar()에 대한 추가 지정이 필요하지 않음
    Bar();
}

int harostudio::Bar() 
{
    return 0; 
}

네임스페이스는 동일한 파일 내의 여러 블록 및 여러 파일에서 선언될 수 있습니다. 컴파일러는 전처리 중에 이러한 부분들을 결합하며, 결과적인 네임스페이스에는 모든 부분에서 선언된 모든 멤버가 포함됩니다. 표준 라이브러리의 헤더 파일에서 선언된 std 네임스페이스가 이러한 예의 하나입니다.

명명된 네임스페이스의 멤버는 선언된 네임스페이스 외부에서도 명시적으로 정의될 수 있습니다. 그러나 정의는 선언이 있는 네임스페이스를 포함하는 네임스페이스에서 선언 이후에 나와야 합니다. 아래의 예시를 살펴보세요.

// defining_namespace_members.cpp
// C2039 expected
namespace V {
    void f();
}

void V::f() { }        // 정상
void V::g() { }        // C2039, g()는 아직 V의 멤버가 아님

namespace V {
    void g();
}

이 오류는 네임스페이스 멤버가 여러 헤더 파일에 걸쳐 선언되었고, 해당 헤더를 올바른 순서로 포함하지 않았을 때 발생할 수 있습니다.

표준 라이브러리의 std 네임스페이스:

모든 C++ 표준 라이브러리의 유형 및 함수는 std 네임스페이스 또는 std 내에 중첩된 네임스페이스에서 선언됩니다.

생성:

네임스페이스 선언은 전역 수준에서만 나타납니다.
네임스페이스 선언은 다른 네임스페이스 내에서 중첩될 수 있습니다.
네임스페이스 선언에는 접근 지정자(공용 또는 비공용)가 포함되지 않습니다.
네임스페이스 정의의 끝에는 세미콜론을 제공할 필요가 없습니다.
하나의 네임스페이스 정의를 여러 단위로 나눌 수 있습니다.

// 네임스페이스 생성
#include <iostream>
using namespace std;

namespace ns1
{
    int value() { return 5; }
}

namespace ns2
{
    const double x = 100;
    double value() { return 2 * x; }
}

int main()
{
    // ns1 내의 value 함수에 접근
    cout << ns1::value() << '\n';

    // ns2 내의 value 함수에 접근
    cout << ns2::value() << '\n';

    // 변수 x에 직접 접근
    cout << ns2::x << '\n';

    return 0;
}

출력:

5
200
100

중첩된 네임스페이스:

중첩된 네임스페이스는 다음과 같이 하나의 네임스페이스 내에서 다른 네임스페이스를 정의할 수 있습니다.

namespace namespace_name1 {
   // 코드 선언
   namespace namespace_name2 {
      // 코드 선언
   }
}

// 중첩된 네임스페이스의 멤버에 접근하기 위해 해결 연산자 사용
using namespace namespace_name1::namespace_name2;
using namespace namespace_name1;

위의 문장에서 namespace_name1을 사용하면 namespace_name2의 구성원이 다음과 같이 범위 내에서 사용 가능해집니다.

// 첫 번째 네임스페이스
namespace first_space {
   void func() {
      cout << "Inside first_space" << endl;
   }

   // 두 번째 네임스페이스
   namespace second_space {
      void func() {
         cout << "Inside second_space" << endl;
      }
   }
}

// using 문을 통해 first_space의 second_space 함수 호출
using namespace first_space::second_space;
int main () {
   func();

   return 0;
}

인라인 네임스페이스:

일반 중첩된 네임스페이스 대신 인라인 네임스페이스의 구성원은 부모 네임스페이스의 구성원으로 처리됩니다. 이 특성은 오버로드된 함수에 대한 하위 명시적 충돌 쿼리를 활성화하여 부모 및 중첩된 인라인 네임스페이스에서 오버로드된 함수에 작동하게 합니다. 또한 인라인 네임스페이스에 선언된 템플릿에 대한 부모 네임스페이스에서의 특수화를 선언할 수 있도록 합니다.

// Header.h
#include <string>

namespace Test
{
    namespace old_ns
    {
        std::string Func() { return std::string("Hello from old"); }
    }

    inline namespace new_ns
    {
        std::string Func() { return std::string("Hello from new"); }
    }
}

#include "header.h"
#include <string>
#include <iostream>

int main()
{
    using namespace Test;
    using namespace std;

    string s = Func();
    std::cout << s << std::endl; // "Hello from new"
    return 0;
}

네임스페이스 별칭

인라인 네임스페이스를 활용하여 공개 인터페이스의 변경을 관리할 수 있습니다. 단일 상위 네임스페이스를 만들고 해당 네임스페이스 내에 각 인터페이스 버전을 캡슐화할 수 있습니다. 최신 또는 선호하는 버전을 가진 네임스페이스는 인라인으로 지정되며, 따라서 상위 네임스페이스의 직접 멤버로 노출됩니다. 코드를 작성하는 클라이언트는 Parent::Class를 호출하면 자동으로 새 코드에 바인딩됩니다. 이전 버전을 사용하려는 클라이언트는 해당 코드가 있는 중첩된 네임스페이스의 완전한 경로를 사용하여 여전히 액세스할 수 있습니다.

예시:

namespace harostudio
{
    namespace v_10
    {
        template <typename T>
        class Funcs
        {
            // ... 메서드 정의
        };
    }

    inline namespace v_20
    {
        template <typename T>
        class Funcs
        {
            // ... 메서드 정의 및 변경된 인터페이스 추가
        };
    }
}

무명 또는 익명 네임스페이스

명시적인 네임스페이스를 생성하되 이름을 지정하지 않을 수 있습니다. 이를 익명 또는 무명 네임스페이스라고 합니다. 이는 변수 선언을 다른 파일의 코드에 보이지 않게 만들어주어 캡슐화를 제공합니다. 같은 파일의 모든 코드는 익명 네임스페이스의 식별자를 볼 수 있지만, 해당 네임스페이스와 그 식별자는 해당 파일 이외에서는 보이지 않습니다.

예시:

namespace
{
    int MyFunc(){}
}

범위 결정 연산자 (::)

네임스페이스 내에서 선언된 모든 이름을 명시하기 위해 네임스페이스의 이름과 범위 결정 연산자 ::를 사용할 수 있습니다.

예시:

namespace New_space
{
    class X
    {
        static int i;
        public:
        void func();
    };

    // ... 다른 코드 정의

}

// 정적 클래스 변수 초기화
int New_space::X::i=23;      

// 클래스 Y의 정의
class New_space::Y
{
    int a;
    public:
    int getdata()
    {
        // ... 코드 정의
    }

    // 생성자 정의
    Y();   
}

// 생성자 명시적 정의
New_space::Y::Y()   
{
    a=0;
}

C++의 분산된 네임스페이스

C++에서 네임스페이스는 여러 부분으로 나누어 정의할 수 있습니다. 이는 독립적으로 정의된 부분의 총합이므로, 하나의 부분이 다른 파일에서 정의된 이름을 필요로 할 경우 해당 이름은 여전히 그 부분의 범위에서 선언되어야 합니다. 분산된 네임스페이스를 작성할 때는 다른 파일에서 정의된 이름을 명시적으로 선언해야 합니다.

장단점

장점

  • 표준 라이브러리의 기능은 컨테이너, 알고리즘 등과 같은 기본 기능을 포함합니다. 이들이 공개적으로 정의된 이름을 사용한다면, 예를 들어 전역적으로 큐 클래스를 정의한다면 동일한 이름을 충돌 없이 사용할 수 없을 것입니다. 따라서 이들은 변경을 관리하기 위해 sexually_transmitted_disease와 같은 네임스페이스를 만들어 이를 포함시켰습니다.

단점

  • 여러 곳에서 정의된 함수를 호출할 때 모호성이 발생할 수 있습니다. 이를 명확히하기 위해 함수 앞에 NameOfNamespace:: 접두어를 사용해야 합니다. 여러 라이브러리를 사용할 때 덜 위험합니다.

결론

이 글로 네임스페이스를 효과적으로 활용하여 코드를 구성하고 관리하는 데 도움이 되었기를 바랍니다.

Leave a Comment