#4. [레거시 C++ 가이드] 유효 범위
- 이름의 유효 범위는 짧게 유지하라. 중괄호(
{}
) 블록으로 짧게 만들 수 있다.
개요
전역 개체의 경우에는 전체 범위에 영향을 주며, 그렇치 않은 경우는 블록 범위 내에서만 영향을 줍니다.(클래스, 함수, 네임스페이스, 열거형, try-catch() 등)
다음은 전역 변수, 함수의 지역 변수, 블록내에 정의된 변수가 이름이 동일할 경우 어떻게 사용되는지 보여주는 예입니다. 가까운 쪽이 사용됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
namespace {
int x = 1; // 전역 변수. namespace 내에 있어 현 파일에서만 접근 가능
}
TEST(TestClassicCpp, Scope) {
EXPECT_TRUE(x == 1); // 전역 변수
int x = 2;
EXPECT_TRUE(x == 2); // 지역 변수가 전역 x를 가림
{
int x = 3;
EXPECT_TRUE(x == 3); // 블록이 지역 x를 가림. 지역 x에 접근할 방법이 없어요.
::x = 10; // :: 붙여 강제로 전역 접근
}
EXPECT_TRUE(x == 2); // 지역 변수
EXPECT_TRUE(::x == 10); // 전역 변수
}
이름의 유효 범위가 짧으면 좋은 이유
프로젝트 규모가 커지면, 서로 다른 영역에서 동일한 이름을 사용하게 될 확률이 높습니다. 특히 직관적이고 좋은 이름일수록요. 이름이 서로 충돌나거나 가리지 않도록 이름의 유효 범위를 짧게 유지하는게 좋습니다.
특히 이름 충돌시 매크로가 관여되면 대환장 파티가 열릴 수 있습니다.(매크로 상수 참고)
블록을 활용한 유효 범위 통제
정의한 곳과 사용한 곳이 서로 가까운 것이 여러모로 좋습니다.
과거의 C언어처럼 함수 앞 부분에 변수 정의(인스턴스화)를 몰아서 해두고, 한참 뒤에 사용하는 건 좋지 않습니다. 사용하지도 않았는데 미리 비용을 지불하지 마세요.(제로 오버헤드 원칙 참고)
특히 미리 정의된 변수가 사용되기 전에 if()
등의 제어문으로 함수가 중단되면 괜히 정의만 한 셈입니다.
1
2
3
4
5
6
7
8
9
10
11
12
void f() {
int a;
int b;
int c; // 미리 정의해 뒀습니다.
if (a + b == 0) {
return 0; // (△) 비권장. int c 를 정의했지만 사용하지 않습니다.
}
return a + b + c;
}
쓸데없는 비용 지불은 둘째치고, 나중에 위아래 스크롤 하기 바빠질 수도 있습니다.(변수명이 뭐였지? 타입이 뭐였지? 그냥 헝가리안 표기를 할까? 언제 초기화 됐지? 언제 값이 바뀌지?)
많은 변수를 사용하는 함수라면,
1
2
3
4
5
6
7
8
void f() {
int a;
int b;
...
int z;
// a~z를 사용하는 코드들
}
유효 범위를 짧아지도록 다음처럼 블록화 해보세요.
1
2
3
4
5
6
7
8
9
10
11
12
13
void f() {
{
int a;
int b;
// a와 b만 사용하는 코드들
}
{
int c;
int d;
// c와 d만 사용하는 코드들
}
}
깔끔하게 나눠졌다면, 각 블록을 함수화 하시는 것도 좋습니다.
댓글남기기