스토리텔링 개발자

[Effective C++] 37. 상속된 함수의 매개변수 기본값 재정의 문제 본문

Effective C++/Effective C++

[Effective C++] 37. 상속된 함수의 매개변수 기본값 재정의 문제

김디트 2024. 7. 5. 09:48
728x90

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

 

 

 

기본 매개변수 값을 재정의할 시 문제 상황
class Shape
{
public:
    enum ShapeColor { Red, Green, Blue };
    
    virtual void draw(ShapeColor color = Red) const = 0;
    ...
};

class Rectangle : public Shape
{
public:
    virtual void draw(ShapeColor color = Green) const; // 기본 매개변수 재정의!!!
    ...
};

Shape* ps;
Shape* pr = new Rectangle;

pr->darw(); // Rectangle::draw(Shape::Red)를 호출한다!!!!
  • 호출 함수의 경우 동적 타입에서 가져오지만, 매개변수의 경우 정적 타입에서 가져온다.
  • 즉, 기본 매개변수 재정의가 되어있는 경우, 정적 타입에 따라 기본 매개변수가 마구 변경되기에 동작을 예측하기 쉽지 않다!

 

 

 

정적 타입과 동적 타입
  • 정적 타입(static type)
    • 선언문을 통해 그 객체가 갖는 타입.
  • 동적 타입(dynamic type)
    • 현재 그 객체가 진짜로 무엇이냐에 따라 결정되는 타입.
Shape* pr = new Rectangle;
// 정적 타입 : Shape
// 동적 타입 : Rectangle

 

 

 

C++이 이런 방식을 고집하는 이유
  • 런타임 효율 문제
    • 기본 매개변수가 동적으로 바인딩된다면?
      • 런타임 중 가상함수의 기본 매개변수 값을 결정할 방법을 컴파일러 쪽에서 마련해야 한다.
      • 이는 현재 메커니즘보다 느리고 복잡할 것이 분명하다.
    • 기본 매개변수를 사용하는 경우가 한정적이므로 효율을 선택했을 것이다.

 

 

 

파생 클래스가 기본 매개변수 값을 똑같이 제공한다면?
class Shape
{
public:
    virtual void draw(ShapeColor color = Red) const = 0;
    ...
};

class Rectangle : public Shape
{
public:
    virtual void draw(ShapeColor color = Red) const;
    ...
};
  • 문제점
    • 코드 중복이며, 의존성까지 걸려있다.
      • 부모 클래스 draw의 기본 매개변수를 변경한다면?
      • 상속받은 기본 매개변수 값을 재정의하는 꼴이나 마찬가지가 되므로 파생 클래스 쪽도 손봐줘야 한다.

 

 

 

해결 방법
  • 가상 함수 대신 사용할 수 있는 방법들을 활용하자.(항목 35 참조)
  • 비가상 인터페이스 관용구(NVI 관용구)를 사용해 보자.
class Shape
{
public:
    // 비가상 함수에 기본 매개변수 처리
    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;
    ...
};

 

728x90
Comments