[C++] 비가상 함수 인터페이스 (NVI)에 대한 질문
글쓴이: GunSmoke / 작성시간: 금, 2006/12/01 - 4:13오후
EC++ 읽고 있는데요. 항목 35에서 NVI라는 것이 등장합니다. 가상 함수를 private 멤버로 두는 형태로 두고 사용자는 public 비가상 멤버 함수를 통해 이 함수를 호출하는 방법을 의미합니다.
NVI의 예제입니다. healthValue를 public 멤버 함수로 두되 비가상 함수로 선언하고 실제 동작을 맡은 private 가상 함수를 호출하는(doHealthValue) 식으로 구성되어 있습니다.
class GameCharacter { public : int healthValue() const //D클래스는 이 함수를 재정의할 수 없음 { ... //사전동작 int retVal = doHealthValue(); ... //사후동작 return retVal; } ... private : virtual int doHealthValue() const //D클래스는 이 함수를 재정의할 수 있음 { ... //캐릭터 체력치 계산을 위한 알고리즘 구현 } };
이렇게 하면 가상함수가 호출되기 전에 사전, 사후 동작을 끼워놓을 수 있다는 장점이 있다고 합니다.
GameCharacter 클래스를 P클래스로 이렇게 꾸며놓았으니 실재 여러 캐릭터들이 이 클래스를 상속해서 D 클래스로 만들어야 할 것입니다. 각 클래스에서는 GameCharcter 클래스의 private 함수 doHealthValue를 재정의해서 사용해야겠죠? 저는 이 점이 궁금합니다. D 클래스에서는 private 멤버에 액세스 조차 할 수 없건만 어떻게 재정의한 다음에 호출할 수 있다는 말입니까?
D 클래스에서 doHealthValue를 직접 호출할 수는 없지만 public 함수 HealthValue를 통해 호출하면 되는것인가요? 그럼 HealthValue를 호출하면 D 클래스에서 재정의된 doHealthValue가 호출된다는 말입니까?
실재로 어떻게 호출하는지 간단한 코드로 보여주시면 쉽게 이해할 수 있을 것 같아요.
Forums:
GoF가 말하는 Template
GoF가 말하는 Template Pattern하고 같아 보입니다.
doHealthValue()를 private으로 두면 Override가 안되니까
protected의 오기가 아닐까 생각합니다.
private virtual 함수는 특수한 용도(구체 클래쓰 은폐)외에는
못봤읍니다.
___________________________________
Less is More (Robert Browning)
___________________________________
Less is More (Robert Browning)
멤버함수 재정의와
멤버함수 재정의와 멤버의 공개여부는 전혀 상관이 없습니다.
말씀하신 대로 제가
말씀하신 대로 제가 잘못 알고 있었군요.
C++에서 처음으로 실망했습니다.
11.6
The access rules for a virtual function are determined by its declaration and are not affected by the rules for a function that later overrides it.
Access is checked at the call point using the type of the expression used to denote teh object for which the member function is called. The access of the member function in the class in which it was defined is in general not known.
In C++, private only restricts access, it does not restrict visibility in a subclass. With private , it is still possible to redefine a private virtual function from a base class in a subclass. This is not a problem, but you cannot prevent redefinition in a subclass, as you can with the Eiffel frozen mechanism.
In Java you cannot override a private method, but you can overload it.
___________________________________
Less is More (Robert Browning)
___________________________________
Less is More (Robert Browning)
D 클래스에서
D 클래스에서 doHealthValue 를 재정의하게되면, D 로 만든 객체로 healthValue 를 호출할 때 D 의 doHealthValue 가 호출됩니다.
D 클래스에서 C 클래스의 doHealthValue 를 호출할 수 없는 것은 자명한 일이지만, 이렇게 사용하는 목적이 구현과 인터페이스를 분리하는 것이므로, 구현을 재정의하는 목적에 부합되는 곳에 사용하면 좋은 패턴이라고 생각하고 있습니다.
문제는 D 의 doHealthValue 에서 C 의 doHealthValue 구현을 이용하는 것이라 봅니다. D 로 만든 객체로는 순수한 C 의 test ( 즉 C::doHealthValue 를 호출하는 ) 를 사용할 수 없고 이렇게 했다가는 무한 루프에 빠지게 됩니다. 이런 경우는 특별히 C 의 doHealthValue 를 protected 로 하여 파생된 클래스에서만 사용하도록 하면 될 것입니다.
간단하게 C, D1, D2 등을 만드셔서 테스트 해보시면 답은 바로 나오리라 생각합니다.
그 책의 다음 버전인 Exceptional C++ Style 을 보시면 더 자세한 설명을 보실 수 있습니다. 왜 이렇게 사용하는 것이 좋은가를 설명하고 있습니다.
용도: 일을 다른 사람 한테 시켜야 할 경우에
필요한 어떤 클래스가 있고 그걸 다른 사람한테 만들어 달라고 요청해야 할 경우에
아래와 같이 Base를 만들어서 다른 사람한테 주면서, DoTest에 필요한 기능을 요청 합니다.
Derived 클래스를 만들어야 하는 개발자는 DoTest의 기능 뿐만 아니라 DoTest가 어떻게 사용되는지도
알면서 개발을 할수 있다는 장점이 있습니다.
답변 매우
답변 매우 감사합니다. 예제를 통해서 더욱 많은 것을 알게되었습니다.
大逆戰
大逆戰
댓글 달기