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

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

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

핵심 규칙

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

체크리스트

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

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

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

  • C++ 상속 | Inheritance 완벽 가이드
  • C++ 가상 함수 | virtual function 가이드
  • C++ 함수 오버로딩 | Function Overloading
  • C++ using 선언 | using declaration

마치며

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

핵심 원칙:

  1. using 선언 사용
  2. 오버로딩 ≠ 오버라이딩
  3. override 명시

베이스 클래스의 오버로드를 사용하려면 using 선언을 추가하세요.

다음 단계: 이름 은닉을 이해했다면, C++ 상속 가이드에서 더 깊이 배워보세요.


관련 글

  • C++ 시리즈 전체 보기
  • C++ Adapter Pattern 완벽 가이드 | 인터페이스 변환과 호환성
  • C++ ADL |
  • C++ Aggregate Initialization |