-
Effective C++...33일지 2022. 8. 4. 21:25
가상 함수 대신 쓸 것들도 생각해 두는 주세를 시시때때로 길러 두자
가상 함수를 대체할 수 있는 방법들
어떤 공통적인 연산을 수행하는 함수를 구현할 때 가상 함수 대신 다음과 같은 구현도 가능하다.
- 비가상 함수 인터페이스 관용구 비가상 함수로 가상 함수를 감싸서 가상 함수를 감추는 방법
- 함수 포인터 전략 패턴 함수 포인터를 이용해 외부 함수를 사용하는 방법
- tr1::function 전략 패턴 STL 라이브러리를 이용해 일반적인 형태의 함수를 사용하는 방법
- 고전적인 전략 패턴 공통 연산을 수행하는 함수를 가진 클래스 계열을 생성해 사용하는 방법
비가상 함수 인터페이스 관용구 예제와 특징
비가상 함수를 호출할 수 있도록 하고 private 가상 함수를 만들어 상속된 클래스에 따라 구현을 달리할 수 있다.
class GameCharacter {
public:
int healthValue() const
{
...
int retVal = doHealthValue();
...
}
private:
virtual int doHealthValue const { ... };
};
※ 자유롭게 계산 함수를 구현할 수 있으나 호출 순서는 최상위 함수에서 결정 되어 있다.
함수 포인터 전략 패턴 예제와 특징
생성자에서 계산에 사용할 함수의 포인터를 받아 실제 계산 시에 사용한다.
// 실제 계산을 위한 함수
int defaultHealthCalc(const GameCharacter& gc);
class GameCharacter {
public:
// 계산에 사용할 함수 포인터 타입 정의
typedef int (*HealthCalcFunc)(const GameCharacter&);
int healthValue() const { return healthFunc(*this); }
private:
HealthCalcFunc healthFunc;
};
※ 같은 타입의 인스턴스도 계산 함수를 다르게 가질 수 있으며, 게임 실행 도중 처리 함수를 바꿀 수도 있다.
tr1::function 전략 패턴 예제와 특징
함수 포인터 전략 패턴과 동일한 구현이지만 STL 라이브러리를 사용하여 조금 더 범용성을 높였다.
class GameCharacter {
public:
// 기존과 같지만 타입이 tr1::function으로 바뀌었다.
typedef std::tr1::function<int (const GameCharacter&)> HealthCalcFunc;
int healthValue() const { return healthFunc(*this); }
private:
HealthCalcFunc healthFunc;
};
※ STL 라이브러리를 사용하여 단순히 HealthCalcFunc와 같은 함수뿐만 아니라 그러한 형식을 가진 모든 함수를 사용할 수 있다.
고전적인 전략 패턴 예제와 특징
가상 계산 함수를 가지는 클래스를 선언해 필요에 따라 상속해 새로운 클래스를 만들어 사용하고 해당 클래스를 전달해 사용하는 방식이다.
class GameCharacter;
class HealthCalcFunc {
public:
...
virtual int calc(const GameCharacter& gc) const { ... }
...
};
HealthCalcFunc defaultHealthCalc;
class GameCharacter {
public:
explicit GameCharacter(HealthCalcFunc *phcf = &defaultHealthCalc)
: pHealthCalc(phcf) { }
int healthValue() const { return pHealthCalc->calc(*this); }
private:
HealthCalcFunc* pHealthCalc;
};
※ 쉽게 이해할 수 있고 계산 클래스의 파생 클래스로 함수를 조절할 수 있는 가능성을 열어뒀다.