ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Effective C++] 6장(2) - 상속, 그리고 객체 지향 설계
    쾌락없는 책임 (공부)/Effetive C++ 요약본 2022. 11. 30. 11:21
    반응형
    본 카테고리는 프로텍 미디어의 '이펙티브 C++'을 보고 요약하는 카테고리입니다.

    3판을 기준으로 하며 전체 내용이 아닌 간략한 내용만을 요약하고 있습니다.

     

    항목 37 : 어떤 함수에 의해서는 상속받은 기본 매개변수 값은 절대로 재정의하지 말자

    간혹 기존의 가상함수가 기본 매개변수를 가지는 일이 있습니다. 이 경우 하위 클래스에서 재정의 한다 해도 변경되지 않습니다. 이건 정적 바인딩으로 정의가 되기에 무조건 부모 크래스의 기본 매개변수를 가져가게 됩니다.

     

    때문에 비가상 인터페이스(NVI)를 사용하는 게 좋습니다. 가상 함수를 호출하는 public 비가상 함수를 부모 클래스에 만들어 주는 것이죠.

    class Shape{
    public:
    	enum ShapeColor { Red, Green, Blue };
    
    	void draw(ShapeColor color = Red) const
    	{
    		doDraw(color);
    	}
    ...
    private:
    	virtual void doDraw(ShapeColor color) const = 0;  // 진짜 작업은 여기서
    };
    
    class Rectangle : public Shape {
    public:
    	...
    private:
    	virtual void doDraw(ShapeColor color) const;  // 기본 매개변수 값이 없음
    };

     

     

    항목 38 : "has-a"나 "is-implemented-in-terms-of"를 모형화할 때는 객체 합성을 사용하자

    '합성'은 다른 타입의 객체들을 포함하는 경우에 성립하는 관계입니다. 그 외 레이어링, 포함, 통합, 내장 등의 이야기들이 있습니다.

    합성의 경우에는 has-a 관계나 is-implemented-in-terms-of 관계를 사용해야 합니다. 이렇게 2개로 나눠진 이유는 영역이 다르기 때문인데요.

     

    • 객체가 우리 일상을 본뜨는 것이라면 객체들은 소프트웨어 응용 영역에 속하게 됩니다.
    • 그 외 나머지들의 경우 소프트웨어 구현 영역에 속하게 됩니다.

    그래서 합성이 응용 영역이면 has-a, 구현 영역이면 is-implemented-in-terms-of 가 됩니다.

     

     

    항목 39 : private 상속은 심사숙고해서 구사하자

    public은 is-a 관계라고 했습니다. 그런데 이를 private 상속을 하게 되면 컴파일러가 파생 클래스 객체를 기본 클래스 객체로 변환하지 않으며 모든 함수는 private 처리됩니다.

     

    그래서 필자는 가급적이면 is-implemented-in-terms-of 로 하고 꼭 해야 한다면 private 상속을 하라고 했습니다. 여기서 '꼭 해야 할 때'는 언제를 의미할까요. 주로 비공개 멤버를 접근하거나 가상 함수를 재정의할 경우를 의미하게 됩니다. 그리고 공간 문제로 private 상속을 하는 경우도 있습니다.

     

    그리고 추가적으로 데이터가 전혀 없는 클래스를 사용할 때를 의미하기도 한답니다. 이런 공백 클래스는 진짜 비어있는게 아니라 typdef, enum, 비가상 함수 등을 포함할 수 있기에 이런 경우도 있다! 라는걸 알아두면 좋습니다.

     

    뭐 이런저런 복잡한 경우들이 있고 대부분의 경우 다른 방법으로 해결이 됩니다. 그러니 private 상속이 가장 좋은 방법이라는 결론이 나면 사용하는걸로 합시다.

     

     

    항목 40 : 다중 상속은 심사숙고해서 사용하자

    C++은 다중 상속이 된다는 사실을 알고 있어야 합니다. 이때 부모 클래스에 시그니처가 중복되는 함수가 있다던지 등의 문제나 다이아몬드 문제가 나올 수도 있습니다. 최상단의 기본 클래스 데이터 멤버가 경로 수만큼 중복 생성되기 때문입니다. 

     

    일단 이런 중복 생성의 경우에는 가상 기본 클래스를 만들어두는게 좋습니다.

    class File {...};
    class InputFile : virtual public File {...};
    class OutputFile : virtual public File {...};
    class IOFile : public InputFile, public OutputFile {...};

    그런데 이런 가상 상속은 비싼 동작으로 용량도 커지고 접근 속도도 느려지게 됩니다. 또한 사용법도 복잡합니다. 때문에 구태여 이런 방법을 쓰기보다는 인터페이스 클래스를 활용해서 이를 사용하는 게 좋습니다.

     

    C++에서 다중 상속이 되는건 장점일 수 있는데 생각할 경우가 상당히 많기 때문에 유지보수를 위해서 인터페이스를 활용하거나 아님 단일 상속을 주로 사용하게 됩니다.

    반응형

    댓글

Designed by Tistory.