항목 32. public 상속 모형은 반드시 "is-a(...는...의 일종이다.)"를 따르도록 만들자.
- public 상속의 의미는 "is-a(...는 ...의 일종)"입니다. 기본 클래스에 적용되는 모든 것들이 파생 클래스에 그대로 적용되어야 합니다. 왜냐하면 모든 파생 클래스 객체는 기본 클래스 객체의 일종이기 때문입니다.
항목 33. 상속된 이름을 숨기는 일은 피하자
- 유효 범위 검색 방법
- 지역 유효 범위 (local scope)
- 객체 유효 범위
- base 클래스 유효 범위
- 네이스페이스 유효 범위
- 전역 유효 범위
- 기본 클래스로 부터 오버로드 버전을 상속 받으려면 '상속된 이름 가리기'를 무시하자.
- '상속된 이름 가리기'를 무시할려면, using 선언을 써서 보이도록 할 수 있다.
- 파생 클래스의 이름은 기본 클래스의 이름을 가립니다. public 상속에서는 이런 이름 가림 현상은 바람직하지 않습니다.
- 가려진 이름을 다시 볼 수 있게 하는 방법으로, using 선언 혹은 전달 함수를 쓸 수 있습니다.
항목 34. 인터페이스 상속과 구현 상속의 차이를 제대로 파악하고 구별하자.
- 멤버 함수 인터페이스는 항상 상속되게 되어 있기 때문에 추상 클래스는 파생된 클래스에 절대적인 영향을 미친다.
- 순수 가상 함수를 선언하는 목적은 파생 클래스에게 함수의 인터페이스만을 물려주려는 것이다.
- 순수 가상함수도 정의를 제공할 수 있다.
- 사용할때는 static 함수처럼 ::로 사용가능 하다.
- 단순 가상 함수를 선언하는 목적은 파생 클래스로 하여금 함수의 인터페이스만 아니라 그 함수의 기본 구현도 물려받게 하는 것이다.
- 비가상 함수를 선언하는 목적은 파생 클래스가 함수 인터페이스와 더불어 그 함수의 필수적인 구현(mandatory implementation)을 물려받게 하는 것이다.
- 인터페이스 상속은 구현 상속과 다릅니다. public 상속에서, 파생 클래스는 항상 기본 클래스의 인터페이스를 모두 물려받습니다.
- 순수 가상 함수는 인터페이스 상속만을 허용합니다.
- 단순(비순수) 가상 함수는 인터페이스 상속과 더불어 기본 구현의 상속도 가능하도록 지정합니다.
- 비가상 함수는 인터페이스 상속과 더불어 필수 구현의 상속도 가하도록 지정합니다.
항목 35. 가상 함수 대신 쓸 것들도 생각해 두는 자세를 시시때때로 길러 두자.
- 비가상 함수 인터페이스(non-virtual interface : NVI ) 관용구 - 템플릿 메서드(template method) 패턴.
- 가상 함수 은폐론
- 함수 기능은 파생 클래스에게 위임하고, 함수를 호출하는 시점은 기본 클래스만의 고유 권한으로 만든다.
- 함수 포인터로 구현한 전략 패턴
- 전략(strategy pattern) 패턴의 단순한 응용
- tr1::function으로 구현한 전략 패턴
- 호환되는 시그니처를 가진 함수 호출성 개체를 사용할 수 있도록 만든 전략 패턴의 한 형태.
- 고전적인 전략 패턴
- 한쪽 클래스 계통에 있는 가상 함수를 다른 쪽 계통에 속해 있는 가상 함수로 대체하는 전통적인 전략 패턴.
- 가상 함수 대신에 쓸 수 있는 다른 방법으로 NVI 관용구 및 전략 패턴을 들 수 있습니다. 이 중 NVI 관용구는 그 자체가 템플릿 메서드 패턴의 한 예 입니다.
- 객체에 필요한 기능을 멤버 함수로부터 클래스 외부의 비멤버 함수로 옮기면, 그 비멤버 함수는 그 클래스의 public 멤버가 아닌 것들을 접근할 수 없다는 단점이 생깁니다.
- tr1::function 객체는 일반화된 함수 포인터처럼 동작합니다. 이 객체는 주어진 대상 시그니처 호환되는 모든 함수호출성 개체를 지원합니다.
항목 36. 상속받은 비가상 함수를 파생 클래스에서 재정의하는 것은 절대 금물!
- 비가상 함수는 정적 바인딩(static binding)으로 묶인다.
- 가상 함수는 동적 바인딩(dynamically binding)으로 묶인다.
- 비가상 멤버 함수는 클래스 파생에 관계없는 불변동작을 정해 두는 것이다.
- 상속받은 비가상 함수를 재정의하는 일은 절대로 하지 맙시다.
항목 37. 어떤 함수에 대해서도 상속받은 기본 매개변수 값은 절대로 재정의하지 말자.
- 가상함수는 동적으로 바인딩(late binding) 되지만, 기본 매개변수는 정적으로 바인딩(early binding)된다.
- 기본 매개변수 값을 부모 클래스와 똑같이 제공하기도 힘들다.
- 코드 중복
- 의존성 (부모 클래스의 기본 매개변수를 바꾸면 파생 클래스도 다 같이 바꿔줘야 한다.)
- 비가상 인터페이스 관용구(non-virtual interface : NVI)를 쓴다.
- 상속받은 기본 매개변수 값은 절대로 재정의해서는 안 됩니다. 왜냐하면 기본 매개변수 값은 정적으로 바인딩되는 반면, 가상 함수(여러분이 오버라이드할 수 있는 유일한 함수이죠)는 동적으로 바인딩되기 때문이다.
항목 38. "has-a(...는...를 가짐)" 혹은 "is-implemented-in-terms-of(...는...를 써서 구현됨)"를 모형화할 때는 객체 합성을 사용하자.
- 합성(composition)
- 레이어링(layering), 포함(containment), 통합(aggregation), 내장(embedding)
- 응용 영역(application domain)
- 우리 일상을 본뜬 객체(사람, 이동수단...)
- 구현 영역(implementation domain)
- 순수하게 시스템 구현을 위한 인공물(버퍼, 뮤텍스..)
- 객체 합성이 응용 영역에서 일어나면 has-a 관계
- 개체 합성이 구현 영역에서 일어나면 is-implemented-in-terms-of 관계
- set 을 list를 가지고 구현할 경우 is-a 관계는 부적합하고, 대신 is-implemented-is-terms-of 관계를 이용하여 구현 하면 간단하다.
- 객체 합성(complsition)의 의미는 public 상속이 가진 의미와 완전히 다릅니다.
- 응용 영역에서 객체 합성의 의미는 has-a입니다. 구현 영역에서는 is-implemeted-in-terms-of의 의미를 갖습니다.
항목 39. private 상속은 심사숙고해서 구사하자.
- private 상속은 is-a 관계가 아니다. is-implemented-is-terms-of 관계이다.
- 구현만 물려받고 인터페이스는 전혀 물려받지 않는다.
- 객체 합성보다 private 상속을 쓸경우는 대개 비공개 멤버를 접근할때, 혹은 가상 함수를 재정의할 경우가 주로 사용된다.
- 공간상의 이득을 얻을수 있다.
- 공백 기본 클래스 최적화(empty base optimization: EBO) : 단일 상속하에서만 적용됨
- public 상속과 객체 합성으로 똑같이 구현할수 있다. 그리고 더 자주 쓰인다.
- 클래스를 설계하는데 있어 파생은 가능하게 하되, 파생 클래스에서 가상함수를 재정의 할수 없게 설계 차원에서 막고싶을때
- 컴파일 의존성을 최소화 하고 싶을때
- private 상속의 의미는 is-implemented-in-terms-of 입니다. 대개 객체 합성과 비교해서 쓰이는 분야가 많지는 않지만, 파생 클래스 쪽에서 기본 클래스의 protected 멤버에 접근해야 할 경우 혹은 상속받은 가상 함수를 재정의해야 할 경우에는 private 상속이 나름대로 의미가 있습니다.
- 객체 합성과 달리, private 상속은 공백 기본 클래스 최적화를 활성화 시킬 수 있습니다. 이 점은 객체 크기를 가지고 고민하는 라이브러리 개발자에게 꽤 매력적인 특징이 되기도 합니다.
항목 40. 다중 상속은 심사숙고해서 사용하자.
- 가상 상속(virtual inheritance)을 사용할수 있다.
- 가상 상속을 안쓴거 보다 크기가 크다.
- 속도도 느리다.
- 다중 상속은 단일 상속보다 확실히 복잡합니다. 새로운 모호성 문제를 일으킬 뿐만 아니라 가상 상속이 필요해질 수도 있습니다.
- 가상 상속을 쓰면 크기 비용, 속도 비용이 늘어나며, 초기화 및 대입 연산자의 복잡도가 커집니다. 따라사 가상 기본 클래스에는 데이터를 두지 않는 것이 현실적으로 가장 실용적입니다.
- 다중 상속을 적법하게 쓸 수 있는 경우가 있습니다. 여러 시나리오 중 하나는, 인터페이스 클래스로부터 public 상속을 시킴과 동시에 구현을 돕는 클래스로부터 private 상속을 시키는 것입니다.
이 글은 스프링노트에서 작성되었습니다.
댓글 없음:
댓글 쓰기