Effective C++ (6): Inheritance and Oject-Oritent Design

7853 단어 컴퓨터 기반
Introduction
본 장 은 주로 상속 을 중심 으로 토론 을 전개한다.Public 상속 인지 private 또는 proctected 상속 인지,상속 중 virtual 의 확정 등 을 어떻게 확정 하 는 지,정 보 량 이 비교적 크다.
Rule 32: Make sure public inheritance models “is-a”
is-a 의 모든 관 계 를 만족 시 키 는 것 이 아니 라 Public 계승 을 사용 할 수 있 습 니 다.예 를 들 어 square is-a rectangle.그러나 모든 rectangle 에 적용 되 는 함수 가 square 에 적용 되 는 것 은 아 닙 니 다.이 럴 때 rectangle 을 변경 하여 더욱 일반화 시 키 거나 square 가 rectangle 을 계승 하지 못 하 게 하 십시오.
Remeber:"Public 계승"은"is-a"관 계 를 의미 하 며,base classes 에 적용 되 는 모든 일 은 derived classes 에 적용 된다.
Rule 33: Avoid hiding inherited names
virtual 함 수 는'인터페이스 계승'을 의미 하고 non-virtual 함 수 는'인터페이스 와 실현 이 모두 계승 되 어야 합 니 다'를 의미 합 니 다.
void Derived::mf4(){
    ...
    mf2();
    ...
}

컴 파 일 러 가 함수 이름 을 찾 는 규칙 은 local 역할 영역 을 먼저 찾 은 다음 외곽 역할 영역(Derived Class)을 찾 은 다음 Base Class 를 찾 은 다음 namespace 까지 마지막 으로 전역 역할 영역 으로 이동 하 는 것 입 니 다.
그러나 과부하 함수 의 계승 에 대해 서 는 특별한 점 이 있 습 니 다.예 를 들 어:
class Base{
public:
    virtual void mf1() = 0;
    virtual void mf1(int);
    virtual void mf2();
    void mf3();
    void mf3(double);
    ...
};

class Derived: public Base{
public:
    virtual void mf1();
    void mf3();
    ...
};

위의 코드 는 Derived class 에서 실 현 된 mf3 와 mf1 에서 리 셋 함 수 를 가 려 서 컴 파일 오류 가 발생 합 니 다.
Derived d;
d.mf1(1);   //   
d.mf3(1.0); //   

따라서 리 셋 함수 에 대한 계승 은 하위 클래스 에서 using 또는 forwarding 을 사용 해 야 합 니 다.
class Derived: public Base{
public:
    using Base::mf1;
    using Base::mf3;
    virtual void mf1();
    void mf3();
    ...
};
// or
class Derived public Base{
public:
    virtual void mf1(){
        Base::mf1();
    }
    ...
};

이렇게 하면 목적 을 달성 할 수 있다.
Remeber:
  • Derived classes 의 이름 은 base classes 의 이름 을 가 립 니 다.따라서 같은 이름 을 사용 하 는 것 을 피 하 는 것 이 좋 습 니 다.
  • 과부하 함 수 를 계승 하기 위해 using 성명 식 또는 전달 함수(forwarding function)
  • 를 사용 할 수 있 습 니 다.
    Rule 34: Differentiate between inheritance of interface and inheritance of implementation
    하위 클래스 계승 이 원 하지 않 는 결 성 을 피하 기 위해 서 는 Pure virtual 함수 로 설명 하고 non-virtual 의 default Implement 를 정의 할 수 있 습 니 다.
    class Airplane{
    public:
        virtual void fly(const Airport& dest) = 0;
        ...
    protected:
        void defaultFly(const Airport& dest);
    };
    
    class ModelA: public Airplane{
    public:
        virtual void fly(const Airport& dest){
            defaultFly(dest);
        }
        ...
    };
    

    이렇게 하면 사용자 가 무의식 적 인 상황 에서 부족 한 실현 을 계승 하 는 것 을 피 할 수 있다.Remeber:
  • 인터페이스 계승 은 계승 실현 과 다르다.Public 계승 아래 derived class 는 항상 base class 의 모든 인 터 페 이 스 를 계승 하지만 실현 은 덮어 쓸 수 있다
  • pure virtual 함 수 는 구체 적 으로 인터페이스 계승 만 지정 합 니 다
  • impure 가상 함수 구체 적 으로 지정 한 인터페이스 계승 및 결 성 실현
  • non-virtual 함수 구체 적 으로 지정 한 인터페이스 계승 및 강제 적 계승 실현
  • Rule 35: Consider alternatives to virtual functions
    어떤 때 는 virtual function 대신 비 virtual function 을 고려 할 수 있 습 니 다.의도 적 으로 사용 할 수 없 는 효과 가 있 습 니 다.
    예 를 들 어 함수 지침 을 통 해 Strategy 모델 을 실현 하고 다음은 혈 액량 을 계산 하 는 코드 를 예 로 들 자.
    class GameCharacter;
    int defaultHealthCalc(const GameCharacter& gc);
    class GameCharacter{
    public:
        typedef int (*HealthCalcFunc)(const GameCharacter&);
        explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc): healthFunc(hcf)
        {}
        int healthValue() const
        { return healthFunc(*this); }
    private:
        HealthCalcFunc healthFunc;  // function pointer
    };
    

    이렇게 외부 삽입 함수 지침 을 통 해 주입(IoC)과 반전 제어(DI)에 의존 하 는 것 과 유사 합 니 다.사실 테스트 에 도 편리 합 니 다(GMock 등 도 구 를 잘 활용 할 수 있 습 니 다).
    위의 코드 에 대해 서 는tr1::function로 교체 할 수 있 습 니 다.tr1:function 은 함수 포인터 class 로 호출 가능 한 모든 것(callable enity,즉 함수 포인터,함수 대상 또는 구성원 함수 포인터)을 호 환 할 수 있 습 니 다.위의 것 만 호 환 할 수 있 습 니 다.
    typedef int (*HealthCalcFunc)(const GameCharacter&);
    

    다음으로 변경:
    typedef std::tr1::function HealthCalcFunc; 
    

    됐 습 니 다.
    Remeber:
  • virtual 함수 대체 방안 은 NVI 기법 과 Strategy 디자인 모델 의 다양한 모델
  • 을 포함한다.
  • NVI(non-virtual interface)기법 은 Template Method(c+tempate)와 상 관 없 이 Public non-virtual 구성원 함수 로 접근 성 이 낮은(private 또는 proctected)virtual 함 수 를 패키지 하여 사전 사후 처리
  • 할 수 있 습 니 다.
  • tr1:function 대상 의 행동 은 일반 함수 포인터 와 같 습 니 다.이러한 대상 은 주어진 대상 과 서명 식 호 환 가능 한 모든 호출 물(callable enities)
  • 을 받 아들 일 수 있 습 니 다.
    Rule 36: Never redefine an inherited non-virtual function
    계승 한 non-virtual 함 수 를 다시 정의 하지 마 십시오.같은 non-virtual 함수 에 대해 서 는 이치 에 따라 행동 이 같 기 때문에 그렇지 않 으 면 잘못된 의 미 를 일 으 키 기 쉽 습 니 다.행동 이 다 르 면 virtual 로 바 꾸 십시오.
    Remeber:계 승 된 non-virtual 함 수 를 다시 정의 하지 마 십시오.
    Rule 37: Never redefine a function’s inherited default parameter value
    이것 은 매우 중요 한 규칙 입 니 다.결 성 된 매개 변수 값 은 모두 정적 으로 연결 되 어 있 습 니 다.즉,결 성 된 매개 변수 값 은 호출 자의 유형 에 따라 확 정 됩 니 다.예 를 들 어 다음 코드 와 같 습 니 다.
    class Shape{
    public:
        virtual void print(int num = 1) const = 0;
    };
    
    class Rect: public Shape{
    public:
        virtual void print(int num = 2) const{
            printf("%d
    ", num); } }; int main(){ Shape *p = new Rect; p->print(); }

    실제 출력 은 1 입 니 다.즉,기본 매개 변 수 는 Rect 의 num=2 가 아 닌 Shape 안의 num=1 입 니 다.그래서 virtual 동적 호출 함 수 를 통 해 호출 되 지만 정적 으로 연 결 된 매개 변수 입 니 다.각각 절반 씩 힘 을 내 서 완 료 된 이 작업 입 니 다.
    따라서 가장 좋 은 방법 은 기본 매개 변수의 일 치 를 유지 하 는 것 입 니 다.기본 매개 변 수 는 계승 되 지 않 기 때문에 수 동 으로 일 치 를 유지 하고 하나의 수정 과 통일 적 인 수정 이 필요 합 니 다.또한 문 제 를 해결 하 는 방법 은 앞서 언급 한 NVI(non-virtual interface)기법 을 통 해 이 루어 집 니 다.Public interface 에서 기본 매개 변 수 를 설정 하 는 것 입 니 다.여기 서 는 군말 하지 않 습 니 다.
    Remeber:계승 되 어 온 결 성 된 매개 변수 값 을 다시 정의 하지 마 십시오.결 성 된 매개 변수 값 은 모두 정적 으로 연결 되 어 있 고 virtual 함 수 는 동적 으로 연결 되 어 있 기 때 문 입 니 다.
    Rule 38: Model “has-a” or “is-implemented-in-terms-of” through composition
    Remeber:복합 을 통 해 has-a 또는'무언 가 에 따라 이 루어 진다'
    Rule 39: Use private inheritance judiciously
    Private 상속 은 Public 상속 에 비해 주로 두 가지 차이 가 있 습 니 다.
  • 컴 파일 러 는 derived class 대상 을 base class 대상 으로 자동 으로 변환 하지 않 습 니 다
  • private base class 가 물 려 받 은 모든 멤버 는 대상 에서 private 속성
  • 이 됩 니 다.
    Remeber:
  • Private 상속 은 is-implemented-in-terms of(어떤 물건 에 따라 이 루어 짐)를 의미 합 니 다.복합(coposition)과 유사 합 니 다.절대 다수의 경우 coposition 을 사용 합 니 다.virtual,proctected 멤버 가 관련 되 었 을 때 만 private 상속
  • 을 사용 합 니 다.
    Rule 40: Use multiple inheritance judiciously
    다 중 계승 에 있어 서 먼저 함수 명 은 나 쁜 의 미 를 일 으 키 기 쉽 습 니 다.계승 하 는 두 부모 클래스 에 같은 함수 명 이 있 을 때 사용 하 는 base class 의 함 수 를 밝 혀 야 합 니 다.
    class BaseA{
    public:
        void check();
    };
    
    class BaseB{
    public:
        void check() const;
    };
    
    class Derived: public BaseA, public BaseB
    { ... };
    
    Derived temp;
    temp.check();           //   
    temp.BaseA::check();    //   
    

    '다이아몬드 형 다 중 상속'이 나 왔 을 때 다 중 상속 베이스 클래스 위 에 같은 베이스 클래스 가 공동으로 계승 되 었 기 때문에 가상 상속 으로 해결 해 야 한다.
    class File { ... };
    class InputFile: virtual public File { ... };
    class OutputFile: virtual public File { ... };
    class IOFile: public InputFile, OutputFile { ... };
    

    그 러 니까
  • 필요 하지 않 으 면 virtual bases 를 사용 하지 않 고 평소에 non-virtual 계승
  • 을 사용 합 니 다.
  • 굳이 사용 하려 면 데 이 터 를 두 지 않도록 하 세 요.
  • Remeber:
  • 다 중 상속 은 나 쁜 의 미 를 초래 하기 쉬 우 며,'다이아몬드 형 다 중 상속'이 나타 나 면 virtual 상속
  • 을 사용 해 야 한다.
  • 가상 계승 은 크기,속도,초기 화(및 할당)복잡 도 등 비용 을 증가 합 니 다.
  • 다 중 상속 은 확실히 정확 한 용도 가 있다.그 중의 한 줄 거 리 는'Public 상속 특정한 인터페이스 class'와'private 상속 특정한 협조 실현 클 라 스'의 결합
  • 과 관련된다.
    시리즈
    Effective C++ (1): Accustoming Yourself to C++ Effective C++ (2): Constructors, Destructors, and Assignment Operators Effective C++ (3): Resource Management Effective C++ (4): Designs and Declaration Effective C++ (5): Implementation Effective C++ (6): Inheritance and Oject-Oritent Design Effective C++ (7): Templates and Generic Programming Effective C++ (8): Customizing new and delete Effective C++ (9): Miscellany

    좋은 웹페이지 즐겨찾기