[2026] C++ 이름 은닉 | 함수가 안 보여요 Name Hiding 문제 해결
이 글의 핵심
C++ 이름 은닉의 C++, 함수가, 보여요, 들어가며: 베이스 클래스 함수가 안 보여요를 실전 예제와 함께 상세히 설명합니다.
들어가며: “베이스 클래스 함수가 안 보여요"
"오버로드가 사라졌어요”
C++에서 파생 클래스에서 같은 이름의 함수를 정의하면, 베이스 클래스의 모든 오버로드가 가려지는 이름 은닉(Name Hiding) 문제가 발생합니다. 다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
// ❌ 이름 은닉
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. 이름 은닉이란?
이름 은닉 발생
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
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 선언
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
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 오버라이딩
오버로딩: 같은 이름, 다른 시그니처
아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 코드를 직접 실행해보면서 동작을 확인해보세요.
class MyClass {
public:
void foo(int x) {}
void foo(double x) {} // 오버로딩
void foo(std::string s) {} // 오버로딩
};
오버라이딩: 가상 함수 재정의
아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
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 오버라이딩
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
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); // ❌ 컴파일 에러
}
해결책: 아래 코드는 cpp를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 코드를 직접 실행해보면서 동작을 확인해보세요.
class Derived : public Base {
public:
using Base::foo; // ✅ Base::foo(double) 가져옴
void foo(int x) override {
std::cout << "Derived::foo(int)\n";
}
};
4. 실전 예시
예시 1: 생성자
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
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: 연산자 오버로딩
다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
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++ 상속 가이드에서 더 깊이 배워보세요.