[C++] 5) C++ 스타일 기본 문법 :: 네임스페이스
네임스페이스
"소속 그룹"을 만들어 동명의 객체를 구별하라
"C/C++에서는 같은 스코프에 같은 이름의 전역 변수나 함수를 만들 수 없다."
위 조건을 만족시키며 프로그램을 만드는 것은 프로그램의 규모가 커질 수록 힘든일이 된다.
하여, 이를 구별하고자 등장한 것이 네임스페이스이다.
동일한 함수, 변수, 상수명을 가졌지만 기능이 완전 다른 두 개의 스코프가 존재한다면, 이는 네임스페이스로 구별지어주어 두 가지 네임스페이스가 동시에 사용되는 경우 구별지어 사용해 줄 수 있다.
적고보니 복잡하다. 코드로 보자
개와 고양이 내용이 한 공간에 저장
// Dog
string name;
int hunger;
void Voice() { std::cout << "BARK!!!" << std::endl; }
// Cat
string name; // 오류 : 동명의 변수 존재
int hunger; // 오류 : 동명의 변수 존재
void Voice() { std::cout << "MEOW!!!" << std::endl; } // 오류 : 동명의 함수 존재
위 코드를 보면, Dog와 Cat이라는 두 개의 네임스페이스가 존재한다. 두 객체로 나누어야하는 객체들이 같은 스코프에 있다면 더 밑에 있는 Cat의 모든 객체들은 선언될 수 없다.
네임스페이스로 구별
namespace Dog{
// Dog
string name;
int hunger;
void Voice() { std::cout << "BARK!!!" << std::endl; }
}
namespace Cat{
// Cat
string name;
int hunger;
void Voice() { std::cout << "MEOW!!!" << std::endl; }
}
이를 방지하기 위해 위와 같이 네임스페이스를 나누어 저장하면 된다.
두 네임스페이스를 동시에 사용하기
#include <iostream>
#include <string>
using namespace std;
namespace dog {
string name = "Smile";
int hunger;
void Voice() { cout << "BARK!!!" << endl; }
}
namespace cat {
string name = "Angel";
int hunger;
void Voice() { cout << "MEOW!!!" << endl; }
}
using namespace dog;
using namespace cat;
int main()
{
string pet = name; // dog의 name인가? cat의 name인가?
return 0;
}
이번에는 선언된 네임스페이스를 사용해보려한다. 위 코드를 보면, Dog와 Cat을 같은 스크립트에서 동시에 사용하려 한다.
그런데, pet의 이름을 설정하는 과정에서 Dog와 Cat 모두 name이 존재하여 컴퓨터는 어떤 네임스페이스의 name값을 전달해 주어야 하는지 모르게 된다.
이러한 경우가 발생하면 IDE가 "동일한 변수명이 존재하여, 값을 정의하기 모호하다."라는 에러 사항을 표시한다.
해결 방안
#include <iostream>
#include <string>
using namespace std;
namespace dog {
string name = "Smile";
int hunger;
void Voice() { cout << "BARK!!!" << endl; }
}
namespace cat {
string name = "Angel";
int hunger;
void Voice() { cout << "MEOW!!!" << endl; }
}
using namespace dog;
using namespace cat;
int main()
{
string myDog = dog::name; // dog의 name
string myCat = cat::name; // cat의 name
dog::Voice(); // dog의 voice 함수 호출
cat::Voice(); // cat의 voice 함수 호출
return 0;
}
이러한 상황에서 가장 쉬운 해결 방안은 충돌나는 객체 앞에 어떤 네임스페이스에서 사용할지 '::'연산자를 통해 직접 정하는 것이다.
📌 전역 네임스페이스
네임스페이스 내에 선언되는 모든 값들은 네임스페이스 내의 다른 함수에 네임스페이스 명칭을 선언하지 않아도 된다.
namespace melon {
int weight = 1;
void Grow() {
melon::weight += 3; // 이럴 필요가 없다.
weight += 3;
}
}
네임스페이스와 함수 정의
네임스페이스는 함수 원형(Function Prototype)을 지원한다.
즉, 동일한 네임스페이스 명을 가진다면 서로 다른 파일에 있어도 서로의 함수와 변수를 참조할 수 있게 된다.
네임스페이스 함수 원형 설정
#include <iostream>
#include <string>
using namespace std;
namespace dog {
string name = "Smile";
int hunger;
void Voice(); // 함수 원형 선언
}
namespace dog {
void Voice() { cout << "BARK!!!" << endl; } // 함수 선언
}
using namespace dog;
int main()
{
string myDog = name; // dog의 name
cout << myDog << endl;
Voice(); // dog의 voice 함수 호출
return 0;
}
혹은 '::'연산자를 통해 어느 네임스페이스 소속의 함수인지 설정도 가능하다.
주의할 것은, 네임스페이스 명칭을 함수명 바로 앞에 '::'연산자로 붙여야한다.
네임스페이스 전용 함수 선언
#include <iostream>
#include <string>
using namespace std;
namespace dog {
string name = "Smile";
int hunger;
void Voice();
}
void dog::Voice() { cout << "BARK!!!" << endl; }
using namespace dog;
int main()
{
string myDog = name; // dog의 name
cout << myDog << endl;
Voice(); // dog의 voice 함수 호출
return 0;
}
중첩 네임스페이스
네임스페이스는 다른 네임스페이스 안에서도 선언이 가능하다.
namespace dog {
string name = "Smile";
int hunger;
void Voice(){ cout << "BARK!!!!" << endl; }
namespace master {
string owner;
void Follow() { cout << "Following" << endl; }
}
namespace companions {
vector<string> friends;
void Follow() { cout << "Following" << endl; }
void Play(){ cout << "Playing" << endl; }
void Fight(){ cout << "Fight!!!!" << endl; }
}
}
위 코드를 통해 우리는 dog라는 네임스페이스 안에서도 master와 companions라는 새로운 네임스페이스가 적용된 것을 볼 수 있다.
이러한 내부 네임스페이스는 '::'연산자로 접근이 가능하다.
#include <iostream>
#include <string>
#include <vector>
namespace dog {
std::string name = "Smile";
int hunger;
void Voice(){ std::cout << "BARK!!!!" << std::endl; }
namespace master {
std::string owner = "No one";
void Follow() { std::cout << "Following " << owner << std::endl; }
}
namespace companions {
std::vector<std::string> friends;
void Follow() { std::cout << "Following" << std::endl; }
void Play(){ std::cout << "Playing" << std::endl; }
void Fight(){ std::cout << "Fight!!!!" << std::endl; }
}
}
using namespace std;
using namespace dog;
int main(void) {
string myDog = name;
Voice();
dog::companions::Follow();
dog::companions::Fight();
dog::master::Follow();
dog::master::owner = "Hong";
dog::master::Follow();
return 0;
}
내부 네임스페이스 외부함수 정의
내부 네임스페이스도 외부 함수정의와 동일하게 사용이 가능하다.
#include <iostream>
#include <string>
#include <vector>
namespace dog {
std::string name = "Smile";
int hunger;
void Voice(){ std::cout << "BARK!!!!" << std::endl; }
namespace master {
std::string owner = "No one";
void Follow();
}
namespace companions {
std::vector<std::string> friends;
void Follow();
void Play();
void Fight();
}
}
// dog의 master 네임스페이스의 Follow 함수 정의
void dog::master::Follow() {
std::cout << "Following " << owner << std::endl;
}
// dog의 companions 네임스페이스의 Follow 함수 정의
void dog::companions::Follow() {
std::cout << "Following" << std::endl;
}
// dog의 companions 네임스페이스의 Play 함수 정의
void dog::companions::Play() {
std::cout << "Playing" << std::endl;
}
// dog의 companions 네임스페이스의 Fight 함수 정의
void dog::companions::Fight() {
std::cout << "Fight!!!!" << std::endl;
}
using namespace std;
using namespace dog;
int main(void) {
string myDog = name;
Voice();
dog::companions::Follow();
dog::companions::Fight();
dog::master::Follow();
dog::master::owner = "Hong";
dog::master::Follow();
return 0;
}
네임스페이스 접근법
네임스페이스는 다양한 방법으로 사용이 가능하다.
1. 한정된 이름 접근 (Qualified name)
한정된 이름 접근 방법은 "네임스페이스::요소"의 방법으로 결정된다.
#include <iostream>
#include <string>
#include <vector>
namespace dog {
std::string name = "Smile";
}
using namespace std;
using namespace dog;
int main(void) {
cout << dog::name << endl; // 한정된 접근
return 0;
}
2. 전역 using 선언 접근 (using declaration)
전역 using 접근자는 네임스페이스에 특정 기능을 사용 가능하게 해준다.
#include <iostream>
#include <string>
namespace dog {
std::string name = "Smile";
void Voice() { std::cout << "BARK!!!" << std::endl; }
}
using namespace std;
using dog::name; // name에 대한 접근만 허용
int main(void) {
cout << name << endl; // using dog::name으로 접근이 가능해졌다.
// using으로 접근이 허용되지 않은 VOice함수는 따로 한정 접근을 해야한다.
dog::Voice();
return 0;
}
3. using namespace 접근
이 방법은 언어를 배우는 모든 사람들이 가장 처음 접근하는 방법일 것이다.
using namespace를 통해 해당 네임스페이스 전체를 접근할 수 있는 권한을 부여받게 된다.
#include <iostream>
#include <string>
namespace dog {
std::string name = "Smile";
void Voice() { std::cout << "BARK!!!" << std::endl; }
}
using namespace std;
using namespace dog; // dog의 모든 부분 접근 가능
int main(void) {
cout << name << endl; // dog::name으로 접근이 가능해졌다.
Voice(); // dog::Voice()으로 접근이 가능해졌다.
return 0;
}
4. 함수 부분 접근 허용
함수 부분 접근은 독특하지만 필요에 의하면 사용하기 좋다.
각 함수 내부에 using으로 네임스페이스에 대한 접근 가능 범위를 정해줄 수 있게 된다.
#include <iostream>
#include <string>
namespace dog {
std::string name = "Smile";
void Voice() { std::cout << "BARK!!!" << std::endl; }
}
using namespace std;
int main(void) {
cout << dog::name << endl; // dog::name으로 접근이 가능해졌다.
dog::Voice(); // dog::Voice()으로 접근이 가능해졌다.
using dog::name; // 이 부분부터 아래로 dog의 name 접근이 가능해진다.
cout << name << endl;
dog::Voice(); // dog::Voice()으로 접근이 가능해졌다.
return 0;
}
5. 함수 부분 접근 허용
4번에서 확장된 부분으로 함수내에서 using namespace문으로 해당 네임스페이스에 대한 모든 접근을 허용한다.
#include <iostream>
#include <string>
namespace dog {
std::string name = "Smile";
void Voice() { std::cout << "BARK!!!" << std::endl; }
}
using namespace std;
int main(void) {
cout << dog::name << endl; // dog::name으로 접근이 가능해졌다.
dog::Voice(); // dog::Voice()으로 접근이 가능해졌다.
using namespace dog; // 이 부분부터 아래로 dog의 name 접근이 가능해진다.
cout << name << endl;
Voice();
return 0;
}