C++ 유효 범위(Scope) 정리 및 상속 관계에서의 동작
C++에서 스코프(Scope, 유효 범위) 란 변수나 함수, 클래스 등이 언제 어디서까지 접근 가능한지를 결정하는 규칙 입니다. 스코프를 잘 이해하면 코드의 안정성, 가독성, 유지보수성 이 높아지고, 네이밍 충돌을 방지할 수 있습니다.
1. 스코프의 종류
1.1 블록 스코프 (Block Scope)
1
2
3
4
5
6
7
8
| void func() {
int a = 10;
{
int b = 20;
// b 사용 가능
}
// b는 여기서 사용 불가
}
|
{} 중괄호 내부에서 선언된 변수는 해당 블록 안에서만 유효합니다.- 블록을 벗어나면 자동으로 소멸됩니다.
1.2 함수 스코프 (Function Scope)
- 함수 내부에서 선언된 변수는 해당 함수 안에서만 유효합니다.
- 다른 함수에서는 같은 이름을 재사용할 수 있습니다.
1
2
| void f1() { int x = 1; }
void f2() { int x = 2; } // f1의 x와는 별개
|
1.3 전역 스코프 (Global Scope)
- 함수 외부에서 선언된 변수는 프로그램 전체에서 접근할 수 있습니다.
1
2
| int globalVar = 42;
void func() { std::cout << globalVar; }
|
1.4 클래스 스코프 (Class Scope)
- 클래스 내부에서 정의된 멤버는 클래스 스코프를 갖습니다.
- 클래스 외부에서 접근 시 범위 지정 연산자(::) 사용
1
2
3
4
5
6
7
8
9
| class MyClass {
public:
int value;
void show();
};
void MyClass::show() {
std::cout << value;
}
|
1.5 네임스페이스 스코프 (Namespace Scope)
1
2
3
4
| namespace Math {
int add(int a, int b) { return a + b; }
}
int x = Math::add(1, 2);
|
1.6 정적 스코프 (Static Scope)
- static 변수는 선언된 스코프 내부에서만 접근 가능하지만, 프로그램 종료까지 메모리에 유지됩니다.
1
2
3
4
5
| void counter() {
static int count = 0;
count++;
std::cout << count;
}
|
2. 이름 숨김(Name Hiding)과 Shadowing
- 내부 스코프에서 외부와 같은 이름을 가진 식별자를 정의하면 외부 이름은 숨겨짐
1
2
3
4
5
| int x = 100;
void func() {
int x = 50; // 전역 x는 숨겨짐
std::cout << x; // 50
}
|
3. 상속 관계에서의 스코프 동작
3.1 부모 멤버에 접근하기
- 자식 클래스는 부모 클래스의 public 및 protected 멤버에 접근 가능
- 범위 지정 연산자(::)로 명확하게 접근 가능
1
2
3
4
5
6
7
8
| class Base { public: int x = 10; };
class Derived : public Base {
public:
void show() {
std::cout << x; // Base::x
std::cout << Base::x; // 명시적 접근
}
};
|
3.2 이름 숨김 (Name Hiding)
- 자식 클래스에서 동일한 이름을 정의하면 부모 클래스의 이름은 숨겨짐 (override 아님)
1
2
3
4
5
6
7
8
9
10
| class Base {
public:
int value = 100;
void show() { std::cout << "Base\n"; }
};
class Derived : public Base {
public:
int value = 200;
void show(int x) { std::cout << "Derived: " << x << "\n"; }
};
|
- 위 코드에서
Derived d; d.show();는 오류 (Base::show 숨겨짐)
3.3 using 선언으로 부모 멤버 노출
1
2
3
4
5
| class Derived : public Base {
public:
using Base::show;
void show(int x) { std::cout << "Derived: " << x << "\n"; }
};
|
3.4 접근 제어자에 따른 상속 시 스코프
| 상속 방식 | Base의 public 멤버 | Base의 protected 멤버 | Base의 private 멤버 |
|---|
| public 상속 | public | protected | 접근 불가 |
| protected 상속 | protected | protected | 접근 불가 |
| private 상속 | private | private | 접근 불가 |
4. 다중 상속에서의 스코프 충돌
1
2
3
4
5
6
7
8
9
| class A { public: void f() { std::cout << "A\n"; } };
class B { public: void f() { std::cout << "B\n"; } };
class C : public A, public B {
public:
void test() {
A::f(); // 명시적으로 지정
B::f();
}
};
|
5. 스코프와 this 포인터
- 상속 관계에서
this는 자신의 타입으로 해석됨 - 필요 시 부모 포인터로 변환해 사용할 수 있음
1
2
3
4
5
6
7
8
9
10
11
12
| class Base {
public:
void print() { std::cout << "Base::print()\n"; }
};
class Derived : public Base {
public:
void callBase() {
Base* basePtr = this;
basePtr->print();
}
};
|
6. 요약
| 스코프 종류 | 설명 |
|---|
| 블록 스코프 | {} 내부 유효 |
| 함수 스코프 | 함수 내부 변수 범위 |
| 전역 스코프 | 파일 전체 또는 프로그램 전역에서 접근 가능 |
| 클래스 스코프 | 클래스 멤버 유효 범위 |
| 네임스페이스 스코프 | 특정 네임스페이스 내부 범위 |
| 정적 스코프 | 접근은 제한되지만 메모리는 유지됨 |
| 상속 스코프 | 부모 멤버 상속 및 이름 숨김, 접근 제어자에 따라 다름 |
C++의 스코프는 단순한 변수 접근 범위를 넘어, 상속 관계, 다형성, 네임스페이스 등과 얽혀 복잡하게 동작할 수 있습니다. 이를 명확히 이해하는 것은 안정적인 객체지향 설계에 매우 중요합니다.