C++ 이름 은닉 | "함수가 안 보여요" Name Hiding 문제 해결
이 글의 핵심
C++ 이름 은닉에 대한 실전 가이드입니다.
들어가며: “베이스 클래스 함수가 안 보여요"
"오버로드가 사라졌어요”
C++에서 파생 클래스에서 같은 이름의 함수를 정의하면, 베이스 클래스의 모든 오버로드가 가려지는 이름 은닉(Name Hiding) 문제가 발생합니다.
// ❌ 이름 은닉
class Base {
public:
void foo(int x) {
std::cout << "Base::foo(int): " << x << '\n';
}
void foo(double x) {
std::cout << "Base::foo(double): " << x << '\n';
}
};
class Derived : public Base {
public:
void foo(std::string s) {
std::cout << "Derived::foo(string): " << s << '\n';
}
};
int main() {
Derived d;
d.foo("Hello"); // OK
d.foo(42); // ❌ 컴파일 에러: no matching function
d.foo(3.14); // ❌ 컴파일 에러
}
이 글에서 다루는 것:
- 이름 은닉이란?
- using 선언으로 해결
- 오버로딩 vs 오버라이딩
- 실전 예시
목차
1. 이름 은닉이란?
이름 은닉 발생
class Base {
public:
void foo(int x) {
std::cout << "Base::foo(int)\n";
}
};
class Derived : public Base {
public:
void foo(double x) { // Base::foo(int)를 가림
std::cout << "Derived::foo(double)\n";
}
};
int main() {
Derived d;
d.foo(3.14); // Derived::foo(double)
d.foo(42); // ❌ 컴파일 에러
// error: no matching function for call to 'Derived::foo(int)'
}
이유: 파생 클래스의 foo가 베이스 클래스의 foo를 완전히 가림.
2. using 선언
해결책: using 선언
class Base {
public:
void foo(int x) {
std::cout << "Base::foo(int)\n";
}
void foo(double x) {
std::cout << "Base::foo(double)\n";
}
};
class Derived : public Base {
public:
using Base::foo; // ✅ 베이스 클래스의 foo를 가져옴
void foo(std::string s) {
std::cout << "Derived::foo(string)\n";
}
};
int main() {
Derived d;
d.foo(42); // Base::foo(int)
d.foo(3.14); // Base::foo(double)
d.foo("Hello"); // Derived::foo(string)
}
3. 오버로딩 vs 오버라이딩
오버로딩: 같은 이름, 다른 시그니처
class MyClass {
public:
void foo(int x) {}
void foo(double x) {} // 오버로딩
void foo(std::string s) {} // 오버로딩
};
오버라이딩: 가상 함수 재정의
class Base {
public:
virtual void foo(int x) {
std::cout << "Base::foo\n";
}
};
class Derived : public Base {
public:
void foo(int x) override { // 오버라이딩
std::cout << "Derived::foo\n";
}
};
이름 은닉 vs 오버라이딩
class Base {
public:
virtual void foo(int x) {
std::cout << "Base::foo(int)\n";
}
virtual void foo(double x) {
std::cout << "Base::foo(double)\n";
}
};
class Derived : public Base {
public:
void foo(int x) override { // Base::foo(int) 오버라이딩
std::cout << "Derived::foo(int)\n";
}
// Base::foo(double)는 가려짐!
};
int main() {
Derived d;
d.foo(42); // Derived::foo(int)
d.foo(3.14); // ❌ 컴파일 에러
}
해결책:
class Derived : public Base {
public:
using Base::foo; // ✅ Base::foo(double) 가져옴
void foo(int x) override {
std::cout << "Derived::foo(int)\n";
}
};
4. 실전 예시
예시 1: 생성자
class Base {
public:
Base(int x) {
std::cout << "Base(int)\n";
}
Base(double x) {
std::cout << "Base(double)\n";
}
};
class Derived : public Base {
public:
using Base::Base; // ✅ 베이스 생성자 상속
Derived(std::string s) : Base(0) {
std::cout << "Derived(string)\n";
}
};
int main() {
Derived d1(42); // Base(int)
Derived d2(3.14); // Base(double)
Derived d3("Hello"); // Derived(string)
}
예시 2: 연산자 오버로딩
class Base {
public:
void operator()(int x) {
std::cout << "Base::operator()(int)\n";
}
};
class Derived : public Base {
public:
using Base::operator(); // ✅ 베이스 operator() 가져옴
void operator()(std::string s) {
std::cout << "Derived::operator()(string)\n";
}
};
int main() {
Derived d;
d(42); // Base::operator()(int)
d("Hello"); // Derived::operator()(string)
}
정리
이름 은닉 해결
| 문제 | 해결책 |
|---|---|
| 함수 가려짐 | using Base::func |
| 생성자 가려짐 | using Base::Base |
| 연산자 가려짐 | using Base::operator |
| 오버라이딩 + 은닉 | using + override |
핵심 규칙
- using 선언 (베이스 함수 가져오기)
- 오버로딩 ≠ 오버라이딩
- virtual도 가려짐 (using 필요)
- 생성자도 상속 (using Base::Base)
체크리스트
- 파생 클래스에서 같은 이름의 함수를 정의하는가?
- 베이스 클래스의 오버로드를 사용하는가?
- using 선언을 추가했는가?
- override 키워드를 사용하는가?
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- C++ 상속 | Inheritance 완벽 가이드
- C++ 가상 함수 | virtual function 가이드
- C++ 함수 오버로딩 | Function Overloading
- C++ using 선언 | using declaration
마치며
이름 은닉은 파생 클래스에서 베이스 클래스의 함수가 가려지는 문제입니다.
핵심 원칙:
- using 선언 사용
- 오버로딩 ≠ 오버라이딩
- override 명시
베이스 클래스의 오버로드를 사용하려면 using 선언을 추가하세요.
다음 단계: 이름 은닉을 이해했다면, C++ 상속 가이드에서 더 깊이 배워보세요.
관련 글
- C++ 시리즈 전체 보기
- C++ Adapter Pattern 완벽 가이드 | 인터페이스 변환과 호환성
- C++ ADL |
- C++ Aggregate Initialization |