이름공간의 등장배경
- 프로그램이 대형화되어 가면서 이름의 충돌문제 발생
- 프로그램 개발을 위해 여러 개의 회사가 참여
- 이들은 프로젝트의 규모가 큰 관계로 일을 구분하여 독립적으로 진행
- 구분해야 할 부분을 적절히 나눔
- 6개월 후 모여 구현한 모듈을 하나로 묶고 부족한 부분을 완성할 때가 됨
- 그러나 문제가 생김
- 몇몇 회사에서 동일한 이름의 함수가 있음
- 충돌이 생김
C++에서는 위 문제를 해결하기 위해서 '이름공간(namespace)'라는 문법을 정의해서 이러한 문제에 대한 근본적인 해결책을 제시하고 있다.
이름공간의 기본원리
아래는 이름 충돌을 일으킴
#include <iostream>
void simplefunc(void)
{
std::cout << "BestCom이 정의한 함수" << std::endl;
}
void simplefunc(void)
{
std::cout << "ProgCom이 정의한 함수" << std::endl;
}
int main(void)
{
simplefunc();
return 0;
}
그런데 자신만의 이름을 공간을 만들고 이 안에 함수를 정의하거나 변수를 선언한다면
namespace BestComImpl // 'BestComImpl'이 이름공간의 이름
{
// 이름공간 내부
}
namespace ProgComImpl // 'ProgComImpl'이 이름공간의 이름
{
// 이름공간 내부
}
dPwp
#include <iostream>
namespace lab1 // 이름공간 이름
{ // 이름공간 내부
void simplefunc(void)
{
std::cout << "lab1이 정의한 함수" << std::endl;
}
}
namespace lab2 // 이름공간 이름
{ // 이름공간 내부
void simplefunc(void)
{
std::cout << "lab2이 정의한 함수" << std::endl;
}
}
int main(void)
{
lab1::simplefunc(); // 범위지정 연산자(scope resolution operator)
lab2::simplefunc();
return 0;
}
위 예제에서 사용된 연산자 ::을 가리켜 '범위지정 연산자(scope resolution operator)'라 하며, 그 이름이 의미하듯이 이름공간을 지정할 때 사용하는 연산자이다.
이름공간 기반의 함수 선언과 정의의 구분
함수는 선언과 정의를 분리하는 것이 일반적.
'함수의 선언' : 헤더파일
'함수의 정의' : 소스파일
#include <iostream>
namespace lab1
{
void simplefunc(void);
}
namespace lab2
{
void simplefunc(void);
}
int main(void)
{
lab1::simplefunc();
lab2::simplefunc();
return 0;
}
void lab1::simplefunc(void)
{
std::cout << "lab1이 정의한 함수" << std::endl;
}
void lab2::simplefunc(void)
{
std::cout << "lab2이 정의한 함수" << std::endl;
}
동일한 이름공간에 정의된 함수를 호출할 때에는 이름공간을 명시할 필요가 없다.
```cpp
#include <iostream>
namespace lab1
{
void simplefunc(void);
}
namespace lab1
{
void simplefunc2(void);
}
namespace lab2
{
void simplefunc(void);
}
int main(void)
{
lab1::simplefunc();
return 0;
}
void lab1::simplefunc(void)
{
std::cout << "lab1이 정의한 함수" << std::endl;
simplefunc2(); // 동일 이름 공간, 직접 호출 가능
lab2::simplefunc(); // 다른 이름 공간
}
void lab1::simplefunc2(void)
{
std::cout << "func2" << std::endl;
}
void lab2::simplefunc(void)
{
std::cout << "lab2이 정의한 함수" << std::endl;
}
이름공간의 중첩
#include <iostream>
namespace Parent
{
int num = 2;
namespace SubOne
{
int num = 3;
}
namespace SubTwo
{
int num = 4;
}
}
int main() {
std::cout << Parent::num << std::endl;
std::cout << Parent::SubOne::num << std::endl;
std::cout << Parent::SubTwo::num << std::endl;
}
논리적인 형태를 보임
총 3개의 num이 존재하면서, 각각 선언된 이름공간이 다르기 때문에 이름출동 문제가 발생하지 않음.
std::cout, std::cin, std::endl
:: 연산자의 의미를 이해했기 때문에 아래가 뜻하는 바를 알 수 있음
std::cout : "이름공간 std에 선언된 cout"
std::cin : "이름공간 std에 선언된 cin"
std::endl : "이름공간 std에 선언된 endl"
헤더파일 <iostream>에 선언되어 있는 cout, cin 그리고 endl은 이름공간 std 안에 선언되어 있다는 결론을 내릴 수 있음
using을 이용한 이름공간의 명시
이제 cout, cin, 그리고 endl을 참조할 때마다 std::을 앞에 붙여야 하는 이유를 알게 됨.
그러나 조금 귀찮음
추가적인 선언 하나만으로도 불편함을 해소할 수 있음
#include <iostream>
namespace Hybrid
{
void HybFunc(void)
{
std::cout << "So simple function!" << std::endl;
std::cout << "In namespace Hybrid!" << std::endl;
}
}
int main(void)
{
using Hybrid::HybFunc;
HybFunc(); // using 선언을 통해 이름공간의 지정 없이 HybFunc 함수를 호출
return 0;
}
위 예제와 같이 아래의 선언은
using Hybrid::HybFunc;
"HybFunc을 이름공간 Hybrid에서 찾으라"는 일종의 선언
위 예제는 using을 main함수 안에 적용되어 지역변수와 마찬가지로 선언이 되었기 때문에 그 지역을 벗어나면, 선언의 효력을 읽게됨
따라서 프로그램 전체에 효력을 미치게 하려면 전역변수와 마찬가지로 함수 밖에 선언을 해야 함.
예시
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
int main(void)
{
int num = 20;
cout << "Hello World!" << endl;
cout << "Hello" <<" World!" << endl;
cout << num << ' ' << 'A';
cout << ' ' << 3.14 << endl;
return 0;
}
만약 using 선언을 하는 것이 귀찮다면, 다음의 선언을 통해 '이름공간 std에 선언된 모든 것에 대해 이름공간 지정의 생략'을 명령할 수 있다.
using namespace std;
이름공간 std에 선언된 모든 것에 접근할 떄에는 이름공간의 지정을 생략하겠다.
#include <iostream>
using namespace std;
int main(void)
{
int num = 20;
cout << "Hello World!" << endl;
cout << "Hello" <<" World!" << endl;
cout << num << ' ' << 'A';
cout << ' ' << 3.14 << endl;
return 0;
}
using namespace 선언이 매력적으로 보이고 편한 건 사실이지만, 이렇게 선언을 해버리면, 그만큼 이름충돌이 발생할 확률은 상대적으로 높아진다.
이름공간의 별칭 지정
namespace AAA
{
namespace BBB
{
namespace CCC
{
int num1;
int num2;
}
}
}
상황에 의해 과도하게 중첩이 되었을 때
AAA::BBB::CCC::num1 = 20;
AAA::BBB::CCC::num1 = 30;
위와 같이 num1과 num2에 접근하는 건 불편하다
이런 경우 별칭을 줄 수 있다.
namespace ABC = AAA::BBB::CCC;
AAA::BBB::CCC에 ABC라는 별칭을 주었다.
그럼 아래와 같이 접근 가능
ABC::num1=10;
ABC::num2=10;
예제
#include <iostream>
using std::cout;
using std::endl;
namespace AAA
{
namespace BBB
{
namespace CCC
{
int num1;
int num2;
}
}
}
int main(void)
{
AAA::BBB::CCC::num1 = 20;
AAA::BBB::CCC::num2 = 30;
namespace ABC = AAA::BBB::CCC;
cout << ABC::num1 << endl;
cout << ABC::num2 << endl;
}
범위지정 연산자(Scope Resolution Operator)의 또 다른 기능
지역변수의 이름이 전역변수의 이름과 같을 경우, 전역변수는 지역변수에 의해 가려진다.
int val = 100;
int func(void) {
int val = 20;
val += 3; // 지역변수 val값 3 증가
}
위 코드에서 보듯이 전역변수와 동일한 아름의 지역변수 val이 선언되었기 때문에 이어서 등장하는 문장에서는 지역변수 val의 값을 3 증가시킨다.
그렇다면 전역변수 val에 접근하려면?
"범위지정 연산자"를 사용하면 됨
int val = 100;
int func(void) {
int val = 20;
val += 3; // 지역변수 val값 3 증가
::val+=7; // [전역변수] val값 7 증가
}
참고 : [윤성우 열혈 C++ 프로그래밍] - 대부분의 내용 및 코드는 이 책에서 개인 공부 정리 목적으로 참고하였습니다.
'Language&Framework&Etc > C++' 카테고리의 다른 글
C언어 기반의 C++ 2(02-2) 새로운 자료형 bool (0) | 2020.12.20 |
---|---|
C언어 기반의 C++ 2(02-1) 시작에 앞서 (0) | 2020.12.20 |
C언어 기반의 C++(01-4) 인라인(inline) 함수 (0) | 2020.12.20 |
C언어 기반의 C++(01-2) 함수 오버로딩(Function Overloading) (0) | 2020.12.19 |
C언어 기반의 C++(01-1) printf와 scanf를 대신하는 입출력 방식 (0) | 2020.12.19 |