항목 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 : 인터페이스 상속과 구현 상속의 차이를 제대로 파악하고 구별하자

  • 상속의 개념
    1. 함수 인터페이스의 상속
    2. 함수 구현의 상속
  • 순수 가상 함수
    1. 어떤 순수 가상 함수를 상속받는 구체 클래스가 재선언해야 한다.
    2. 전형적으로 추상 클래스에서 정의를 갖지 않는다.
      • 파생 클래스에게 함수의 인터페이스만 물려준다.
      • 순수 가상 함수에도 정의 제공이 가능하다.(구현을 붙일 수 있다.) 단, 호출 시 클래스 이름을 한정자로 사용해야 한다. 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보다 융통성이 없다.)
  • 비가상 함수 : 클래스 파생에 상관 없이 변하지 않는 동작을 지정한다.
    • 파생 클래스가 인터페이스와 그 함수의 필수적인 구현을 물려받는다.
  • 멤버 함수 선언 시 결정적인 실수
    1. 모든 멤버 함수를 비가상 함수로 선언한다 : 비가상 소멸자 문제가 있다(7)
    2. 모든 멤버 함수를 가상 함수로 선언한다 : 클래스 파생에 상관 없는 불변 동작은 구현하자.