[2026] C++ 이름 은닉 | 함수가 안 보여요 Name Hiding 문제 해결

[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. 이름 은닉이란?
  2. using 선언
  3. 오버로딩 vs 오버라이딩
  4. 실전 예시
  5. 정리

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

핵심 규칙

  1. using 선언 (베이스 함수 가져오기)
  2. 오버로딩 ≠ 오버라이딩
  3. virtual도 가려짐 (using 필요)
  4. 생성자도 상속 (using Base::Base)

체크리스트

  • 파생 클래스에서 같은 이름의 함수를 정의하는가?
  • 베이스 클래스의 오버로드를 사용하는가?
  • using 선언을 추가했는가?
  • override 키워드를 사용하는가?

같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.


마치며

이름 은닉파생 클래스에서 베이스 클래스의 함수가 가려지는 문제입니다. 핵심 원칙:

  1. using 선언 사용
  2. 오버로딩 ≠ 오버라이딩
  3. override 명시 베이스 클래스의 오버로드를 사용하려면 using 선언을 추가하세요. 다음 단계: 이름 은닉을 이해했다면, C++ 상속 가이드에서 더 깊이 배워보세요.

관련 글

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3