[ Effective C++ ] 항목 35 : 가상 함수 대신 쓸 것들도 생각해 두는 자세를 시시때때로 길러두자

Minsu._.Lighting·2023년 12월 9일
0

[ Effective C++ ] 정리 모음집
" C++ 프로그래머의 필독서, 스콧 마이어스의 Effective C++ 를 읽고 내용 요약 / 정리 "

[핵심]

" 어떤 문제를 해결하기 위한 설계를 찾을 때 가상 함수를 대신하는 방법들도 고려해보자! "

  • 가상 함수 대신에 쓸 수 있는 다른 방법으로 NVI 관용구 및 전략 패턴을 들 수 있으며 이 중 NVI 관용구는 그 자체가 템플릿 메서드 패턴의 한 예이다!
  • 객체에 필요한 기능을 멤버 함수로부터 클래스 외부의 비멤버 함수로 옮기면, 그 비멤버 함수는 그 클래스의 public 멤버가 아닌 것들을 접근할 수 없다는 단점이 생긴다!
  • tr1::function 객체는 일반화된 함수 포인터처럼 동작한다. 이 객체는 주어진 대상 시그니처와 호환되는 모든 함수호출성 개체를 지원한다.

💡 비가상 인터페이스 관용구를 통한 메서드 패턴

class GameCharacter
{
public:
	int healthValue() const
    {
    ...
    int retVal = doHealthValue();
    ...
    return retVal;
    }
    
	...

private:
	virtual int doHealthValue() const;
    {
    	...
    }
}:
  • 비가상 인터페이스 관용구(NVI)를 사용
    - 공개되지 않은 가상 함수를 비가상 public 멤버 함수로 감싸서 호출하는 템플릿 메서드 패턴의 한 형태

  • 어쨌든 가상 함수를 사용하기에 눈속임과 같다...



💡 함수 포인터로 구현한 전략 패턴

class GameCharacter;

int defaultHealthCalc(const GameCharacter& gc);

class GameCharacter
{
public:
	typedef int (*HealthCalcFunc) (const GameCharacter&);
    
    explicit GameCharacter(HealthCalFunc hcf = defaultHealthCalc)
    : healthFunc(hcf)
    {}
    
    int healthValue() const
    { return healthFunc(*this); }
    ...
private:
	HealthCalFunc healthFunc;
};
  • 가상 함수를 함수 포인터 데이터 멤버로 대체
    - 전략(sttrategy)패턴의 단순한 응용

  • 같은 타입으로 만들어진 객체들도 체력치 계산 함수를 다르게 가릴 수 있다

class EvilBadGuy : public GameCharacter
{
public:
	explicit EvilBadGuy(HealthCalcFunc hcf = defaultHealthCalc)
    : GameCharacter(hcf)
    { ... }
};

int loseHealthQuickly(loseHealthQuickly);
int loseHealthSlowly(loseHealthSlowly);
  • 게임이 실행되는 도중 특정 캐릭터에 대한 체력치 계산 함수를 바꿀 수 있다

📌 단점

  • 대상 객체의 비공개 데이터는 이 함수로 접근할 수 없다


💡 tr1::function으로 구현한 전략패턴

class GameCharacter
int defaultHealthCalc(const GameCharacter& gc);

class GameCharacter
{
public:
	typedef function<int (const GameCharacter&)> HealthCalcFunc;
    
    explicit GameCharacter(HealthCalcFunc hcf = defalutHealthCalc)
    : healthFunc(hcf)
    {}
    
    int healthValue() const
    { return healthFunc(*this); }
    ...
private:
	HealthCalcFunc healthFunc;
}:
  • 가상 함수를 tr1::function 데이터 멤버로 대체
    - 호환되는 시그니처를 가진 함수 호출성 개체를 사용할 수 있도록
    - 전략 패턴의 한 형태


💡 "고전적인" 전략 패턴

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;
};
  • 한쪽 클래스 계통에 속해 있는 가상 함수를 다른 쪽 계통에 속해 있는 가상 함수로 대체
profile
오코완~😤😤

0개의 댓글

관련 채용 정보