프로그래밍C++ 개발자

C++에서 가상 상속이란 무엇이며, 왜 필요한가요?

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

답변.

문제의 역사

C++에서 클래스 계층은 다이아몬드 문제(diamond problem)를 유발할 수 있습니다. 하나의 기본 클래스에서 두 개의 자식 클래스가 상속받고, 그 다음에 이 두 클래스에서 상속받는 또 다른 클래스가 생성될 때 발생합니다. 이 경우 자식 클래스의 객체는 두 개의 독립적인 기본 클래스 복사본을 포함하게 됩니다. 이 문제를 해결하기 위해 C++에서는 가상 상속을 구현했습니다.

문제

일반적인 다중 상속을 사용하는 경우:

class A { public: int x; }; class B : public A {}; class C : public A {}; class D : public B, public C {};

객체 D는 A의 두 복사본을 포함하며: 하나는 B를 통해, 다른 하나는 C를 통해 접근합니다. 이는 A::x에 대한 접근의 모호성과 불필요한 메모리 낭비를 야기합니다.

해결책

가상 상속은 기본 클래스의 중복을 제거합니다. 기본 클래스 A의 한 인스턴스는 모든 자식 클래스가 공유합니다:

class A { public: int x; }; class B : public virtual A {}; class C : public virtual A {}; class D : public B, public C {};

이제 D는 A의 한 복사본만 가지며, A::x에 대한 접근의 모호성이 제거됩니다.

주요 특징:

  • 다이아몬드 문제를 방지합니다.
  • 가상 상속은 기본 클래스의 멤버에 대한 접근 속도를 느리게 합니다.
  • 가상 기본 클래스의 생성자는 가장 "먼" 자식의 생성자에 의해 호출됩니다.

속임수 질문.

범위 해석(scope resolution)을 통해 다이아몬드 문제를 해결할 수 없는 이유는 무엇인가요?

범위 해석은 기본 클래스 멤버에 대한 접근의 모호성만을 해결할 뿐, 기본 클래스의 불필요한 복사본을 제거하지는 않습니다. 이중 초기화 및 이중 데이터 저장 문제는 여전히 남아 있습니다.

가상 상속에서 생성자는 어떤 순서로 호출되나요?

가상 기본 클래스의 생성자는 가장 먼저 호출되며, 오직 한 번만 호출되며, 상속 계층에서 가장 마지막 클래스의 생성자에 의해 직접 호출됩니다.

예:

class A { public: A() { std::cout << "A "; } }; class B : public virtual A { public: B() { std::cout << "B "; } }; class C : public virtual A { public: C() { std::cout << "C "; } }; class D : public B, public C { public: D() { std::cout << "D "; } }; D d; // 출력: A B C D

클래스 선언 및 정의에서 가상 상속을 반드시 선언해야 하나요?

네, 가상 상속은 해당 기본 클래스에서 상속받을 때마다 명시해야 합니다. 그렇지 않으면 컴파일 오류가 발생하며, 다중 상속이 일반적인 결과가 됩니다.

전형적인 오류 및 안티 패턴

  • 상속할 때 virtual을 지정하지 않음 — 기본 클래스의 두 인스턴스가 생성됩니다.
  • 모든 중간 클래스에서 가상 기본 클래스의 생성자를 호출하려는 시도 — 컴파일 오류를 발생시킵니다.
  • 가상 상속을 남용하면 계층 구조와 코드 유지 관리가 복잡해집니다.

실제 사례

부정적인 예

가상 상속이 적용되지 않은 복잡한 계층 구조로 인해 객체에 두 개의 설정 복사본이 포함되어 있습니다:

장점:

  • 쉽게 컴파일하고 실행할 수 있습니다.

단점:

  • 구성 데이터에 쉽게 혼동을 일으킬 수 있으며, 기본 데이터가 "마법적으로" 복제되는 결함으로 이어질 수 있습니다.

긍정적인 예

가장 상위 자식의 일관된 생성자와 함께 가상 상속을 사용했습니다:

장점:

  • 기본 데이터 중복이 없으며, 동작이 명확하고 단순합니다.

단점:

  • 가상 상속에 대한 경험이 없는 개발자에게는 분석 및 디버깅에 추가적인 복잡성이 발생할 수 있습니다.