상속 (Inheritance)¶
구성이 상속보다 더 적절한 경우가 많습니다. 상속을 사용할 때는 공개로 설정하세요.
하위 클래스가 기본 클래스에서 상속되면 기본 클래스가 정의하는 모든 데이터 및 작업의 정의가 포함됩니다. "인터페이스 상속"은 순수 추상 기본 클래스(상태나 정의된 메서드가 없는 클래스)로부터의 상속입니다. 다른 모든 상속은 "구현 상속"입니다.
구현 상속은 기존 유형을 전문화하므로 기본 클래스 코드를 재사용하여 코드 크기를 줄입니다. 상속은 컴파일 타임 선언이므로 사용자와 컴파일러는 작업을 이해하고 오류를 감지할 수 있습니다. 인터페이스 상속을 사용하면 클래스가 특정 API를 노출하도록 프로그래밍 방식으로 적용할 수 있습니다. 다시 말하지만, 컴파일러는 클래스가 API의 필수 메소드를 정의하지 않은 경우 오류를 감지할 수 있습니다.
구현 상속의 경우 하위 클래스를 구현하는 코드가 기본 클래스와 하위 클래스 사이에 분산되어 있으므로 구현을 이해하기가 더 어려울 수 있습니다. 하위 클래스는 가상이 아닌 함수를 재정의할 수 없으므로 하위 클래스는 구현을 변경할 수 없습니다.
다중 상속은 종종 더 높은 성능 오버헤드를 부과하고(실제로 단일 상속에서 다중 상속으로의 성능 저하가 일반에서 가상 디스패치로의 성능 저하보다 더 클 수 있음) 모호성, 혼란 및 명백한 버그가 발생하기 쉬운 "다이아몬드" 상속 패턴으로 이어질 위험이 있기 때문에 특히 문제가 됩니다.
모든 상속은 공개되어야 합니다. 비공개 상속을 수행하려면 대신 기본 클래스의 인스턴스를 멤버로 포함해야 합니다. 클래스를 기본 클래스로 사용하는 것을 지원하지 않으려는 경우 클래스에 final을 사용할 수 있습니다.
구현 상속을 과도하게 사용하지 마세요. 구성이 더 적절한 경우가 많습니다. 상속 사용을 "is-a" 사례로 제한해 보세요. Bar가 Foo의 "일종"이라고 합리적으로 말할 수 있는 경우 Bar는 Foo를 하위 클래스로 지정합니다.
protected 사용을 하위 클래스에서 액세스해야 할 수 있는 멤버 함수로 제한하세요. 데이터 멤버는 private이어야 합니다.
재정의 또는 (덜 자주) 최종 지정자 중 정확히 하나만 사용하여 가상 함수 또는 가상 소멸자의 재정의에 명시적으로 주석을 답니다. 재정의를 선언할 때 virtual을 사용하지 마세요. 근거: 기본 클래스 가상 함수의 재정의가 아닌 override 또는 final로 표시된 함수 또는 소멸자는 컴파일되지 않으며 이는 일반적인 오류를 잡는 데 도움이 됩니다. 지정자는 문서 역할을 합니다. 지정자가 없으면 독자는 해당 클래스의 모든 조상을 확인하여 함수나 소멸자가 가상인지 아닌지 확인해야 합니다.
다중 상속은 허용되지만 다중 구현 상속은 권장되지 않습니다.
이해하기 쉽게 설명하기¶
핵심: 상속보다 구성(composition)을 먼저 고려하라¶
"is-a" 관계가 분명할 때만 상속하세요. Bar가 Foo의 "일종"이라고 자연스럽게 말할 수 있어야 합니다. 그저 코드를 재사용하려는 것이라면 상속이 아니라 멤버로 포함(구성)하는 편이 대개 낫습니다.
인터페이스 상속 vs 구현 상속¶
- 인터페이스 상속: 순수 추상 기본 클래스(상태·구현 없음)로부터의 상속. 특정 API를 강제하고 컴파일러가 누락을 잡아줍니다.
- 구현 상속: 그 외. 구현이 기본 클래스와 하위 클래스에 흩어져 이해하기 어려워지므로 과용하지 마세요.
모든 상속은 public¶
private 상속이 필요하다면 그건 사실 "포함" 관계입니다 — 기본 클래스 인스턴스를 멤버로 두세요. 하위 클래스를 막고 싶으면 클래스에 final을 쓸 수 있습니다.
override / final 로 명시¶
가상 함수를 재정의할 때는 override(또는 드물게 final)를 정확히 하나 붙이고, virtual은 다시 쓰지 마세요. 재정의가 아닌데 override를 붙이면 컴파일 오류가 나서 흔한 실수를 잡아줍니다. 또한 독자가 조상 클래스를 다 뒤지지 않아도 가상 여부를 알 수 있는 문서 역할을 합니다.
class Base {
public:
virtual ~Base() = default;
virtual void Foo();
};
class Derived : public Base {
public:
void Foo() override; // virtual 반복 X, override O
};
그 밖에¶
protected는 하위 클래스가 접근해야 하는 멤버 함수로만 제한하세요. 데이터 멤버는 private이어야 합니다.- 다중 상속은 허용되지만 다중 구현 상속은 권장되지 않습니다(다이아몬드 상속의 모호성·버그 위험).