[Effective C++] 항목 33 ~ 34
항목 33 : 상속된 이름을 숨기는 일은 피하자
- c++의 이름 가리기 규칙은 겹치는 이름들의 타입과 무관하게 가린다.
- 심지어 받아들이는 매개변수 타입, 가상 / 비가상 함수 여부도 무관하게 가린다.
- 가려진 이름은 using 선언으로 끄집어낼 수 있다.
- ex)
class Base { public: virtual void mf1() = 0; virtual void mf1(int); void mf3(); void mf3(double); } class Derived : public Base { public: using Base::mf1; // mf1(int)가 가려지므로 using Base::mf3; // mf3(double)이 가려지므로 virtual void mf1(); void mf3(); };
- 기본 클래스가 가진 함수를 전부 상속했으면 하는 것이 아닌 경우(private 상속) using 선언은 그 이름에 해당하는 것이 모두 파생 클래스로 내려가므로 해결이 불가능하다.
- 전달 함수 사용(암시적으로 인라인 함수가 된다.)
- ex)
virtual void mf1() { Base::mf1(); }
- 상속이 템플릿과 엮일 경우 다른 문제가 발생한다.(43)
항목 34 : 인터페이스 상속과 구현 상속의 차이를 제대로 파악하고 구별하자
- 상속의 개념
- 함수 인터페이스의 상속
- 함수 구현의 상속
- 순수 가상 함수
- 어떤 순수 가상 함수를 상속받는 구체 클래스가 재선언해야 한다.
- 전형적으로 추상 클래스에서 정의를 갖지 않는다.
- 파생 클래스에게 함수의 인터페이스만 물려준다.
- 순수 가상 함수에도 정의 제공이 가능하다.(구현을 붙일 수 있다.) 단, 호출 시 클래스 이름을 한정자로 사용해야 한다. ex)ps1->shape::draw();
- 단순(비순수)가상 함수 : 파생 클래스에서 오버라이드 가능한 함수 구현부도 제공한다.
- 파생 클래스에게 함수의 인터페이스와 그 함수의 기본 구현도 물려준다.
- 단순 가상 함수 구현 시 파생 클래스에서 그 기본동작을 원한다고 명시하지도 않았는데 그 동작을 물려받는 게 걸림돌이 없는 것이 문제이다.
- 순수 가상 함수로 구현하고 원래 제공하던 기본 구현을 별도의 함수로 제공한다.
- ex)
virtual void fly(const Airport& destination);
- => 다음과 같이 변경
virtual void fly(const Airport& destination) = 0; protected: void defaultFly(const Airport& destination);
- 순수 가상 함수를 정의하는 기능을 활용하여 따로 비가상함수(defaultFly())를 준비하지 않는 방법도 가능하다.
- 보호 수준이 날아간다.(fly는 public인데 구현했으므로 protected보다 융통성이 없다.)
- 비가상 함수 : 클래스 파생에 상관 없이 변하지 않는 동작을 지정한다.
- 파생 클래스가 인터페이스와 그 함수의 필수적인 구현을 물려받는다.
- 멤버 함수 선언 시 결정적인 실수
- 모든 멤버 함수를 비가상 함수로 선언한다 : 비가상 소멸자 문제가 있다(7)
- 모든 멤버 함수를 가상 함수로 선언한다 : 클래스 파생에 상관 없는 불변 동작은 구현하자.