프로그래밍C++ 시스템/인프라 개발자

함수 및 변수의 상속 시 이름 조회 (name lookup), 범위 (scope) 순서에 대해 설명하고, 특히 가상 상속에서 계층 구조 내 이름의 모호성으로 인해 발생하는 문제는 무엇인지. 이러한 충돌을 어떻게 해결합니까?

Hintsage AI 어시스턴트로 면접 통과

답변

C++에서 이름 조회 순서는 이름 해결 규칙(name lookup)에 의해 규제되며, 이는 범위(scope), 숨김(hiding) 및 상속의 특성을 고려합니다. 주요 사항:

  • 메소드 호출/변수 접근 시 검색은 파생 영역에서 기본 영역으로 진행됩니다.
  • 다중 상속의 경우, 이름이 두 개의 기본 분기에 모두 나타나면 모호성(ambiguity)이 발생합니다.
  • 가상 상속은 중복(다이아몬드 문제)을 피하는 데 도움이 되지만, 이름을 명시적으로 해결해야 합니다.

충돌 해결:

  • 자식 클래스에서 숨겨진 함수의 경우, using을 사용할 수 있습니다:
class Base { public: void foo(int) {} }; class Derived : public Base { public: using Base::foo; void foo(double) {} // 오버로드된 버전 };
  • 같은 이름의 변수 또는 함수의 경우:
    • 기본 클래스의 이름을 명시적으로 지정해야 합니다: Base::x 또는 Base::foo().

다이아몬드 상속의 모호성 예:

struct A { int x; }; struct B : virtual A {}; struct C : virtual A {}; struct D : B, C { void test() { x = 42; } // OK: virtual 덕분에 x는 하나 };

함정 질문

기본 클래스에 메소드 foo(int)가 선언되고 파생 클래스에 foo(double)가 선언된 경우, 파생 클래스의 객체에서 foo(int)에 접근할 수 있습니까?

답변: 아니요, 메소드 foo(double)가 기본 클래스의 모든 오버로드된 버전을 "숨기고" 있습니다. foo(int)에 접근하려면 using Base::foo 또는 호출에서 Base::foo를 명시적으로 작성해야 합니다.

예:

class Base { public: void foo(int) { } }; class Derived : public Base { public: void foo(double) {} }; Derived d; d.foo(3); // 오류! foo(int)는 보이지 않음

사용:

class Derived : public Base { public: using Base::foo; void foo(double) {} }; d.foo(3); // 모든 것이 작동함

주제에 대한 미세한 차이로 인한 실제 오류 예


이야기

다중 상속 프로젝트에서 동일한 메소드를 가진 두 개의 기본 클래스로부터 상속받은 클래스가 생겼습니다. 저자들은 가상 상속을 사용하지 않았으며 기본 클래스의 두 개의 복사본이 생성되었습니다 — 모든 foo() 호출은 모호해졌고, 컴파일러는 코드를 컴파일하지 않았습니다. 이는 가상 상속을 적용하고 호출 시 Base::foo()를 명시함으로써 해결되었습니다.


이야기

그래픽 엔진에서 하나의 자식 클래스가 기본 클래스의 draw()에 대해 using을 사용하지 않고 자신의 draw()를 정의했습니다. 코드 리팩토링 후 잘못된 버전의 draw()가 호출되어 인터페이스의 일부가 그려지지 않았습니다. 이 오류는 클래스 구조를 깊이 있게 분석한 후에야 발견되었습니다.


이야기

새로운 컴파일러로 이동하면서 다중 및 가상 상속 시 이름의 모호성으로 인해 컴파일이 불가능해졌습니다. 이전에 작업했던 구현이 포함 체인의 차이를 고려하지 않았으며, 이는 번역 단위에서 서로 다른 이름 집합을 초래했습니다.