질문의 배경:
C++는 C++ 언어와 객체 지향 방법론에서 클래스 상속 개념을 상속받았습니다. 다중 상속과 가상 상속의 출현은 계층 구조의 복잡성을 증가시켜 유연성을 증가시켰지만 새로 발생하는 오류 유형도 추가했습니다.
문제:
직접 상속은 클래스가 다른 클래스로부터 추가 복잡성 없이 직접 상속받는 상황입니다. 간접 상속은 상속된 멤버가 하나 이상의 중간 상속 체인을 통해 전달될 때 발생합니다. 주요 어려움은 "다이아몬드 문제"(diamond problem)로, 여러 경로가 하나의 기본 클래스에 접근할 수 있어 파생 클래스에 기본 클래스의 멤버가 중복될 수 있습니다.
해결책:
복잡성을 관리하기 위해 가상 상속이 사용되며, 이는 전체 계층 구조에서 기본 클래스 멤버의 공통 인스턴스를 제공하고 각 체인이 아닌 공통 인스턴스를 제공합니다.
코드 예시:
class A { public: int value; }; class B : public virtual A {}; class C : public virtual A {}; class D : public B, public C {};
클래스 D에서는 A::value의 인스턴스가 하나만 존재합니다.
주요 특징:
가상 상속을 사용하지 않으면 다이아몬드 문제로 인해 정의되지 않은 동작이 발생할 수 있습니까?
다이아몬드 계층에서 가상 상속을 사용하지 않으면 기본 클래스의 멤버가 중복될 수 있습니다. 이는 코드 논리를 혼란스럽게 하고 기본 클래스 멤버에 접근할 때 모호성을 초래할 수 있습니다.
가상 상속을 사용할 필요가 없는 경우는 언제입니까?
여러분의 계층 구조가 다이아몬드 구조를 형성하지 않거나 기본 클래스에 데이터가 포함되지 않는 경우, 가상 상속이 필요하지 않습니다.
가상 상속 시 생성자 호출을 어떻게 관리합니까?
가상 기본 클래스의 생성자는 "가장 낮은" 파생 클래스에서만 호출됩니다. 중간 클래스에서 가상 기본 클래스에 대한 인자를 명시하는 것은 금지되어 있으며 (최종 클래스에서 명시적으로 초기화되지 않으면 생성자가 기본적으로 호출됨), 이로 인해 오류가 발생할 수 있습니다.
코드 예시:
class A { public: A(int x) { /* ... */ } }; class B : public virtual A { public: B() : A(0) {} // 오류: 여기서 A를 초기화할 수 없음 }; class D : public B { public: D() : A(10), B() {} // 올바름 };
큰 프로젝트에서 개발자는 다이아몬드 구조의 출현을 간과했습니다 — 두 개의 중간 클래스가 하나의 기본 클래스에 직접 상속되었습니다. 기본 클래스의 멤버에 접근하는 것은 모호성을 유발하고, 코드는 읽기 어렵게 되었습니다.
장점:
단점:
건축가는 문제를 사전에 인식하고 중간 클래스에 가상 상속을 사용했으며, 가상 기본 클래스의 생성자는 최하위 파생 클래스에서만 올바르게 호출되었습니다.
장점:
단점: