나만의 작은 도서관
[C++] 탐구 일지 #1. 소속을 지정하는 네임스페이스(namespace) 본문
네임스페이스란?
네임스페이스(namespace)는 한국말로 "이름 공간"이며, 단어에서 알 수 있듯 사용한 객체(함수나 변수와 같은)가 소속된 공간의 이름을 말한다. 코드의 양이 많아지거나 많은 사람들이 협력하는 프로젝트인 경우, 같은 이름으로 선언한 함수들이 발생할 수 있는데, 이 때 네임스페이스를 이용하여 같은 이름의 함수들을 구분할 수 있다.
네임스페이스의 사용하는 방법은 아래 코드와 같다.
네임스페이스 선언부
// namespace 네임스페이스_이름{
// 코드1
// 코드2
// ...
// }
namespace myNamespace{
int x = 123;
void print(){
std::cout << "myNamespace" << std::endl;
}
void print2(); // 선언만 하는 것도 가능하다.
}
네임스페이스에 정의된 객체 사용법
// 네임스페이스_이름::함수_이름
int main(){
myNamespace::print(); // myNamespace
cout << myNamespace::x << '\n'; // 123
}
using namespace std;
std는 C++ 표준 라이브러리의 모든 함수 객체등이 정의된 네임스페이스다. 표준 라이브러리에 있는 객체들을 사용하고싶다면 반드시 사용할 객체 앞에 std::를 붙여야된다.
예를 들어, "Hello World"를 표준 라이브러리를 이용해 콘솔에 출력하고 싶다면, 표준 라이브러리에 존재하는 <iostream> 헤더를 현재 파일에 포함하고(#include), std::cout을 이용한다. 코드는 다음과 같다.
#include <iostream>
int main(){
std::cout << "Hello World" << std::endl; // Hello World
}
여기서 조금 번거러운 점은 표준 라이브러리에 존재하는 객체들을 쓸 때마다 std::를 앞에 붙여야된다는 것이다. 이러한 번거로움을 해결하기 위해 using namespace std;를 사용하면 현재 파일 범위(scope)내에서 std::를 생략할 수 있다.
#include <iostream>
using namespace std; // using 키워드 사용
int main(){
cout << "Hello World" << endl; // Hello World
}
전역 네임스페이스와의 충돌
using namespace를 사용하면 객체를 사용할 때 소속된 네임스페이스 이름을 생략할 수 있지만, 전역 네임스페이스에 선언된 객체와 이름 충돌이 발생할 수 있다는 문제가 있다.(전역 네임스페이스는 전역 함수나 전역 변수가 선언되는 공간이라 보면 된다.)
아래 코드처럼 using 키워드를 사용한 상황에서 이름과 시그니처가 완전히 동일한 print() 함수를 호출하면, 컴파일러는 어느 네임스페이스의 print() 함수인지 구분할 수 없어 오류를 뱉어내게 된다.
#include <iostream>
namespace myNamespace{
void print(){
std::cout << "myNamespace" << std::endl;
}
}
void print(){
std::cout << "Global Namespace" << std::endl;
}
using namespace myNamespace;
int main(){
print(); // 오류: 오버로드된 함수 "print"의 인스턴스 중 두 개 이상이 인수 목록과 일치합니다.
}
따라서, using namespace를 사용할 때엔 충돌이 발생하는지 반드시 확인해야한다(이런 이유로 using namespace 사용은 일반적으로 권장하지 않는다. 하지만, 표준 라이브러리만 사용하는 코딩 테스트와 같은 환경에선 사용해도 문제없다.)
using namespace는 여러 개 사용할 수 있다.
놀랍게도 using namespace는 여러 개 사용할 수 있다. 따라서, 아래와 같이 코드를 짜도 오류는 발생하지 않는다. (당연하지만 각 네임스페이스에 같은 시그니처를 가진 객체를 선언하면 충돌이 발생한다.)
#include <iostream>
namespace myNamespace{
void print(){
std::cout << "myNamespace" << std::endl;
}
}
namespace myNamespace2{
void print2(){
std::cout << "myNamespace2" << std::endl;
}
}
using namespace myNamespace;
using namespace myNamespace2;
int main(){
print(); // myNamespace
print2(); // myNamespace2
}
std만 생략한 ::cout
using namespace를 사용했을 때, 가끔가다 ::을 남기는 경우가 있다. ::을 남긴다고 해서 (cout을 예로 들자면) std::cout이나 cout으로 표기했을 때와 결과가 달라지진 않는다. 그럼에도 ::cout을 사용하는 이유는 코드를 작성한 사람의 의도를 남기는데 의의가 있기 때문이다. ::을 의도적으로 남기면 "해당 객체는 std에 있는 객체입니다."를 의미하게 된다. 이는 std에 있는 객체를 오버라이딩하는 경우처럼 std 객체인지 오버라이딩한 객체인지 분간하기 쉽게하기 위한 용도로 사용할 수 있다.
#include <iostream>
using namespace std;
int main(){
cout << "cout" << '\n'; // cout
::cout << "::cout" << '\n'; // ::cout
std::cout << "std::cout" << '\n'; // std::cout
}
참고 자료
'C++ > 문법 및 메소드(STL)' 카테고리의 다른 글
[C++] 탐구 일지 #2. 타입의 별칭: typedef, using 키워드 (0) | 2024.12.21 |
---|---|
[문자열] 문자열을 정수로 stoi(), 숫자를 문자열로 to_string() (0) | 2024.03.21 |
[문자열] str1 == str2 는 TRUE? (0) | 2023.09.10 |
[문자열] 일부 문자열 반환 substr(), 문자열 길이 반환 length() (0) | 2023.06.25 |
[문자열] 특정 문자열 찾기 find(), 문자열 부분 교체 replace() (0) | 2023.06.24 |