Scope
block
scope을 생성하는 가장 간단한 형태
int main()
{
int a = 0; // scope of the first 'a' begins
++a; // the name 'a' is in scope and refers to the first 'a'
{
int a = 1; // scope of the second 'a' begins
// scope of the first 'a' is interrupted
a = 42; // 'a' is in scope and refers to the second 'a'
} // block ends, scope of the second 'a' ends
// scope of the first 'a' resumes
} // block ends, scope of the first 'a' ends
int b = a; // Error: name 'a' is not in scope
Namespace
mdn web doc 에서는 다음과 같이 정의하고 있다.
네임스페이스 (Namespace)는 프로그램에서 사용되는 이름의 논리적 그룹인 식별자의 컨텍스트입니다. 동일한 컨텍스트 및 범위 내에서, 식별자는 엔터티를 고유하게 식별해야 합니다.
정의 및 특징
namespace <name>{...}형태로 정의- 일반적으로 헤더 파일에서 정의
// header1.h
namespace header1 {
int foo();
void bar();
}
- 중첩 선언 가능
- namespace1:: namespace2::someting
- <name> 생략 시 해당 파일 안에서만 접근 가능
- static 선언과 같은 효과
- 하나의 파일도 거대한 namespace 상 코드
namespace N { // scope of N begins (as a member of global namespace)
int i; // scope of i begins
int g(int a) { return a; } // scope of g begins
int j(); // scope of j begins
void q(); // scope of q begins
namespace {
int x; // scope of x begins
} // scope of x does not end
inline namespace inl { // scope of inl begins
int y; // scope of y begins
} // scope of y does not end
} // scope of i,g,j,q,inl,x,y interrupted
namespace {
int l=1; // scope of l begins
} // scope of l does not end (it's a member of unnamed namespace)
namespace N { // scope of i,g,j,q,inl,x,y continues
int g(char a) { // overloads N::g(int)
return l+a; // l from unnamed namespace is in scope
}
int i; // error: duplicate definition (i is already in scope)
int j(); // OK: repeat function declaration is allowed
int j() { // OK: definition of the earlier-declared N::j()
return g(i); // calls N::g(int)
}
int q(); // error: q is already in scope with different return type
} // scope of i,g,j,q,inl,x,y interrupted
int main() {
using namespace N; // scope of i,g,j,q,inl,x,y resumes
i = 1; // N::i is in scope
x = 1; // N::(anonymous)::x is in scope
y = 1; // N::inl::y is in scope
inl::y = 2; // N::inl is also in scope
} // scope of i,g,j,q,inl,x,y interrupted
참고 사항
- 네임스페이스 블록 안에서 허용되는 구문은 선언(Declaration) 또는 정의(Definition)
- main 함수가 아니라면 실행이 안되기 때문에 문맥상 오류로 인식
- 위와 마찬가지 이유로 함수 내에서는 namespace 사용 불가
내부 접근방법
<namespace>::<obj>: 범위 지정 연산자(scope resolution operator) 사용using <namespace>::<obj>: nampesapce 없이<obj>로 사용using namespace <namespace>: 해당 scope에서 namespace 생략
namespace N
{
int n;
void fn(){
printf("fn\n");
}
} // namespace N
namespace N
{
void fn(char* c){
printf("N::n = %d\n", n);
}
// fn(); // error, 명시적 선언이 없음(즉 선언문으로 인식)
} // scoping namespace N
int main() {
using N::fn;
using namespace std;
fn();
fn("s");
cout << "namespace std" << "\n";
}
>>>
fn
N::n = 0
namespace std
Function & Parameter
함수는 일반적으로 지역 범위 생성.
parameter의 경우 () 안에서는 scoping 불가
void fn(int a, int b=a){}; // 식별자 "a"이(가) 정의되어 있지 않습니다.
Class
하나의 class는 자체적인 scope을 가진다.
즉 class 자신을 나타내는 this 없이 멤버 변수에 접근이 가능하다.
class 외부에서는 범위 지정 연산자(scope resolution operator)::을 통해 scope을 생성한다.
class X {
int f(int a = n) { // X::n is in scope inside default parameter
return a*n; // X::n is in scope inside function body
}
using r = int;
r g();
int i = n*2; // X::n is in scope inside initializer
// int x[n]; // Error: n is not in scope in class body
static const int n = 1;
int x[n]; // OK: n is now in scope in class body
};
//r X::g() { // Error: r is not in scope outside of out-of-class member function body
auto X::g()->r { // OK: trailing return type X::r is in scope
return n; // X::n is in scope in out-of-class member function body
}
int f(int a = n): class 전체 scope에서 n 선언을 인식- 단 static 없이 n을 선언(int n;)하게 된다면 n은 멤버함수가 돼 오류가 발생
- 인스턴스 없이는 존재하지 않는 값이기 때문
기타
goto & label
goto를 통한 label의 이동은 선언의 순서와 관계 없다.
void f()
{
{
goto label; // label in scope even though declared later
label:;
}
goto label; // label ignores block scope
}
void g()
{
goto label; // error: label not in scope in g()
}
try, catch
try-catch블록은 각각 block scope를 형성한다.
catch 절은 바로 앞의 try 블록과 연결되며, 내부 변수는 각 블록 내에서만 유효하다.
try블록 안에서 선언된 변수는try블록 내에서만 유효catch절에서 선언된 변수도 해당catch블록 안에서만 유효- 각각 별도의 block scope를 형성
int main() {
try {
int a = 10;
throw a;
} catch (int e) {
std::cout << "Caught exception: " << e << std::endl;
}
// std::cout << a << std::endl; // ❌ Error: a는 try 블록 밖에서 scope 벗어남
// std::cout << e << std::endl; // ❌ Error: e도 catch 블록 안에서만 유효
}
Lifetime
static
static키워드는 수명(lifetime)은 전체 프로그램,
scope은 정의된 block(함수, 클래스, 파일)에 제한되는 저장 클래스 지정자이다.
- 함수 내 static 변수는 해당 함수가 끝나도 메모리에 남아있으며 다음 호출 시 그대로 사용됨
- 이 때 값의 할당은 최초 1회만 실행
- 파일 내부에서
static전역 변수나 함수는 해당 translation unit에서만 접근 가능 (internal linkage)
void foo() {
static int count = 0; // 함수가 여러 번 호출되어도 count는 한 번만 초기화됨
++count;
std::cout << count << std::endl;
}
// a.cpp
static int hidden = 42; // 다른 파일에서 접근 불가
static void secret() {} // 외부에서 링크 안 됨
extern
다른 translation unit (파일)에서 정의된 변수를 참조할 수 있도록 외부 linkage를 허용
- 선언만 하고 정의는 외부 파일에 있는 전역 변수/함수를 참조할 수 있게 함
- 정의는 한 번만,
extern선언은 여러 곳에서 가능
// a.cpp
int shared = 100;
// b.cpp
extern int shared; // a.cpp의 shared를 참조
요약
| 키워드/구문 | Scope | Lifetime | 특징 요약 |
|---|---|---|---|
block |
{} 내부 |
block 종료 시 | 내부 변수는 밖에서 접근 불가 |
namespace |
명시된 이름 범위 | 프로그램 전체 | 이름 중복 방지, 선언/정의만 가능 |
function |
함수 내부 | 함수 종료 시 | parameter도 local scope |
class |
class 블록 내부 | 객체 수명과 일치 (static은 예외) | 멤버 변수, 멤버 함수 포함 |
try-catch |
try/catch 각각의 block | block 종료 시 | catch 변수는 내부에서만 사용 가능 |
static |
선언 위치(block/fn/file)에 따라 | 프로그램 전체 (1회 초기화) | scope는 제한되나 lifetime은 전역 |
extern |
참조된 파일의 전역 범위 | 참조된 객체와 동일 | 외부 파일 참조 용도 |
C
Contents
