23. 이름 숨김(name hiding)과 오버로드 해석의 스코프 규칙

난이도 최상 해설 보기 →

결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커 (A)(B) 는 주목 위치 힌트다.

결함 코드 · C++
// ============================================================================
// [언어 내부동작 문제] C++ - 이름 숨김(name hiding)과 오버로드 해석의 스코프 규칙
// ----------------------------------------------------------------------------
// 주제: 파생 클래스에서 기반 클래스와 "같은 이름"의 함수를 새로 선언하면,
//   기반 클래스에 있던 그 이름의 오버로드들은 어떻게 되는가?
//   - 이 문제는 "결함을 고치는" 문제가 아니라, C++ 의 동작 원리를 설명하는 문제다.
//   - 게임서버 코드에서 흔한 패턴: 기반 패킷 핸들러 클래스에 Handle(int), Handle(const char*)
//     같은 오버로드를 두고, 파생 클래스가 특정 타입만 재정의(오버라이드/오버로드)하려 한다.
//
// 과제:
//   1) 아래 main 의 각 호출이 컴파일되는지, 컴파일된다면 어떤 함수가 호출되는지 예측하라.
//      컴파일 에러가 나는 줄이 있다면 어떤 줄이고 왜인지 예측하라.
//   2) C++ 이름 조회(name lookup)가 "스코프 단위로 멈춘다"는 것이 무슨 뜻인지,
//      이것이 오버로드 해석(인자 타입 매칭)보다 왜 먼저 일어나는지 설명하라.
//   3) (A) 지점에 무엇을 추가하면 기반 클래스의 오버로드들이 다시 보이게 되는지,
//      그리고 그 메커니즘이 내부적으로 무엇을 하는 것인지 설명하라.
//   4) 이 규칙이 가상 함수 오버라이딩(시그니처가 정확히 같은 경우)과는 어떻게 다르게
//      상호작용하는지 설명하라.
//   (먼저 직접 예측·설명을 적은 뒤 answer.md 와 대조할 것)
// ============================================================================

#include <cstdio>

class PacketHandlerBase {
public:
    virtual ~PacketHandlerBase() = default;

    void Handle(int code) {
        std::printf("Base::Handle(int) code=%d\n", code);
    }
    void Handle(const char* name) {
        std::printf("Base::Handle(const char*) name=%s\n", name);
    }
    virtual void Handle(double value) {
        std::printf("Base::Handle(double) value=%.1f\n", value);
    }
};

class LoginHandler : public PacketHandlerBase {
public:
    // (A) 여기에 기반 클래스의 오버로드 세트를 다시 끌어오지 않았다.
    // Base::Handle(int) 와 Base::Handle(const char*) 는 어떻게 되는가?
    void Handle(int code) {   // 시그니처가 Base::Handle(int) 와 동일하지만 virtual 아님
        std::printf("LoginHandler::Handle(int) code=%d\n", code);
    }

    void Handle(double value) override {  // 진짜 가상 오버라이드
        std::printf("LoginHandler::Handle(double) value=%.1f\n", value);
    }
};

int main() {
    LoginHandler lh;
    lh.Handle(42);            // 어떤 Handle 이 호출되는가?
    lh.Handle(3.14);          // 어떤 Handle 이 호출되는가?
    // lh.Handle("ping");     // 이 줄의 주석을 풀면 무슨 일이 일어나는가? (컴파일 여부 예측)

    PacketHandlerBase* base = &lh;
    base->Handle(42);         // 정적 타입 기준 오버로드 해석 + 동적 디스패치, 무엇이 출력되는가?
    base->Handle(3.14);

    return 0;
}
내 리뷰 · C++
내 답안 · 자동 저장

작성 후 위 해설 보기에서 모범 해설과 대조하세요.