티스토리 뷰

728x90

가상 함수 대신 쓸 것들도 생각해 두는 자세를 시시때때로 길러 두자.

1) 비가상 인터페이스 관용구(non-virtual interface)를 사용하자.
- 공개되지 않은 가상 함수를 비가상 public 멤버 함수로 감싸서 호출하는, 템플릿 메서드 패턴의 한 형태
- NVI에서 가상 함수가 엄격하게 public 일 필요는 없다.
- 장점 : 가상 함수가 호출되기 전에 어떤 상태를 구성하고 가상 함수가 호출된 후에 그 상태를 없애는 작업을 래퍼를
  통해 공간적으로 보장된다.

*비가상 인터페이스 관용구(non-virtual interface)란?
- 사용자로 하여금 public 비가상 멤버 함수를 통해 private 가상 함수를 간접적으로 호출하게 만드는 방법.
- 관용구에 쓰이는 비가상 함수는 가상함수의 래퍼라고 부른다.

 

2) 가상함수를 함수 포인터 데이터 멤버로 대체하자.
- 군더더기 없이 전략 패턴의 핵심만을 보여주는 형태.
- 장점 : 다양한 상황에 대처할 수 있는 유연한 클래스를 만들 수 있다.
- 프로그램 실행 도중 함수를 바꿀 수 있다.
- 단점 : private 로 선언된 변수의 경우 함수가 클래스 내부가 아닌 외부에 있기 때문에 변수에 접근할 수 없는 상황이 벌어진다.

 

3) 가상함수를 tr1::function 데이터 멤버로 대체하여, 호환되는 시그너처를 가진 함수 호출 성 개체를 사용할 수 있도록 만들자
- 전략 패턴의 한 형태.
- 장점 : 함수 포인터 보다 유연한 클래스를 만들 수 있다.(멋진 코드)

4) 한쪽 클래스 계통에 속해 있는 가상 함수를 다른 쪽 계통에 속해 있는 가상 함수로 대체하자.
- 전략 패턴의 전통적인 형태
- 장점 : 표준적인 전략 패턴 구현 방법에 친숙할 경우 빨리 이해할 수 있다.

 

예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
class GameCharacter
{
public:
    virtual int healthValue() const;
};
 
 
 
EX1) NVI 관용구를 통한 템플릿 메서드 패턴
class GameCharacter
{
public:
    int healthValue() const;
    {
        int retVal = doHealthValue();
        return retVal;
    }
private:
   virtual int doHealthValue() const
   {
   }
};
// healthValue(비가상함수)를 doHealthValue(가상함수)의 랩퍼(wrapper)라고 부름
 
 
Ex2) 함수 포인터로 구현
class GameCharacter;   //전방선언
int defaultHealthCalc(const GameCharacter& gc); //이곳에서 계산합니다.
 
class GameCharacter
{
public:
   typedef int (*HealthCalcFunc)(const GameCharacter&);
   explicit GameCharacater(HealthCalcFunc hcf = defaultHealthCalc):healthFunc(hcf){}
   //생성자.
   int healthValue() const
   {
       return healthFunc(*this);
   }
private:
   HealthCalcFunc healthFunc;
};
 
 
Ex3) tr1::function 구현
class Game Character;   //전방선언
int defaultHealthCalc(const GameCharacter& gc); //이곳에서 계산합니다.
class GameCharacter
{
public:
    typedef std::trl::function<int (const GameCharacter& ) > HealthCalcFunc;
    explicit GameCharacater(HealthCalcFunc hcf = defaultHealthCalc):healthFunc(hcf){}
   //생성자.
   int healthValue() const
   {
       return healthFunc(*this);
   }
private:
   HealthCalcFunc healthFunc;
};
 
 
Ex4) 고전적인 전략(Strategy) 패턴
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;
 
};
 
 
 
 
cs

 

결론

가상 함수 대신에 쓸 수 있는 다른 방법으로 NVI 관용구 및 전략 패턴을 들 수 있다.

이 중 NVI 관용구는 그 자체가 템플릿 메서드 패턴의 한 예이다.

객체에 필요한 기능을 멤버 함수로부터 클래스 외부의 비멤버 함수로 옮기면, 그 비멤버 함수는 그 클래스의 public 멤버가 아닌 것들을 접근할 수 없다는 단점이 생긴다.

tr1::function 객체는 일반화된 함수 포인터처럼 동작한다.
이 객체는 주어진 대상 시그너처와 호환되는 모든 함수 호출 성 개체를 지원한다.

 

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함
250x250