KoreanFoodie's Study

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

Tutorials/C++ : Advanced

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

GoldGiver 2022. 10. 25. 16:28

C++ 프로그래머의 필독서이자 바이블인, 스콧 마이어스의 Modern Effective C++ 를 읽고 기억할 내용을 요약하고 있습니다. 꼭 읽어보시길 추천드립니다!

항목 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;
};

class Circle: public Shape
{
public:
	virtual void draw(ShapeColor color) const;
};


int main()
{
	Shape *ps;
	Shape *pc = new Circle;
	Shape *pr = new Rectangle;

	// Rectangle::draw(Shape::Red) 를 호출!
	// Rectangle::draw(Shape::Green) 이 아니다!
	pr->draw();
}

위에서, pr->draw() 가 우리가 기대한 대로 동작하지 않는 원인이 뭘까? 호출되는 가상 함수는 Rectangle 것이 맞지만, pr 의 정적 타입은 Shape* 이기 때문에, 가상 함수에 쓰이는 기본 매개변수 값은 Shape 클래스에서 가져온다! 만약 상속받은 가상 함수의 기본 매개변수 값을 재정의하면 위처럼 기상천외한 동작이 발생하는 것이다.
이는, C++ 가 런타임 효율을 위해 기본 매개변수 값을 정적으로 바인딩하기 때문이다.

그렇다면 파생 클래스에서 가상 함수의 기본 매개변수 값을 어떻게 하면 깔끔하게 받게 만들 수 있을까? 여러 방법이 있지만, 비가상 인터페이스(non-virtual interface) 관용구를 사용하는 것을 추천한다. 예시를 보자.

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 { ... }
};

 

Comments