∇ 프로그래밍 소품 및 팁

 ▼ 임의의 class의 protected에 접근

C++

N/A

남이 만든 class를 사용하는 입장에서는 그 class의 public만 접근 가능하다는 것은 큰 제약 중에 하나이며 특히 내부 변수에 대한 디버깅이 필요할 때는 접근 권한 때문에 많은 불편함을 겪게 된다. 물론 VC++의 디버깅 환경에서는 별 문제없이 private 값까지 볼 수 있지만 embedded 환경 등의 열악한 환경에서는 그것 또한 쉽지 않다.

JAVA나 Delphi의 경우에는 각각 package와 unit이라는 개념을 넣어서 그 안에 속해 있는 그룹끼리는 자동으로 friend 선언이 된 것처럼 동작한다. 하지만 C++이 경우에는 직접 class에 대해 friend 선언을 해주어야 내부 권한에 대한 접근이 가능하게 되고 만약 추가적인 class에 대한 접근을 허용하게 하려면 원본 헤더를 수정해야 하는 불편함이 있다. (특히 자신 소유가 아닌 class에 대해서는 수정이 불가능 할 수 도 있다)

아래는 일반 함수에서 class를 인자를 받아서 protected의 멤버 변수에 접근을 시도하는 소스를 구현한 것이다.

    01: class CPlayer
    02: {
    03: protected:
    04:     int m_hp;
    05: public:
    06:     CPlayer(int hp): m_hp(hp) {};
    07: };
    08:
    09: int GetInternalHP(const CPlayer& a)
    10: {
    11:     return a.m_hp;
    12: }
    13:
    14: #include <iostream>
    15:
    16: int main(void)
    17: {
    18:     CPlayer npc(100);
    19:     std::cout << GetInternalHP(npc) << std::endl;
    20:     return 0;
    21: }

11번 라인에서는 전달된 CPlayer class형으로 받은 파라미터에서 protecte인 멤버 변수를 꺼내려한다. 당연히 이렇게 하면 에러가 난다.

error C2248: 'm_hp' : cannot access protected member declared in class 'CPlayer'

VC++의 경우에는 이런 에러를 내며 g++의 경우에도 같은 류의 에러를 낼 것이다. 그래서 이 에러를 피하면서 합법적으로 protected의 멤버 변수에 접근하기 위해서 다음과 같은 바식을 제안한다.

    01: class CPlayer
    02: {
    03: protected:
    04:     int m_hp;
    05: public:
    06:     CPlayer(int hp): m_hp(hp) {};
    07: };
    08:
    09: int GetInternalHP(const CPlayer& a)
    10: {
    11:     class CHackedPlayer: public CPlayer
    12:     {
    13:     public:
    14:         static int GetHP(const CPlayer& a)
    15:         {
    16:             return static_cast<const CHackedPlayer*>(&a)->m_hp;
    17:         }
    18:     };
    19:     return CHackedPlayer::GetHP(a);
    20: }
    21:
    22: #include <iostream>
    23:
    24: int main(void)
    25: {
    26:     CPlayer npc(100);
    27:     std::cout << GetInternalHP(npc) << std::endl;
    28:     return 0;
    29: }

11라인부터 18라인의 부분이 핵심인데, CPlayer로부터 상속받은 CHackedPlayer는 합법적으로 CPlayer의 protected를 꺼낼 수 있고, CHackedPlayer에서 public static으로 선언된 함수는 일반 함수와 같으면서도 CPlayer의 protected를 꺼낼 수 있다. 그리고 문법적인 문제를 피하기 위해서 CPlayer를 CHackedPlayer로 static cast를 했는데, 이 두 class는 실제로 아무 것도 더해지지 않았으므로 그 구조가 동일하며 static cast로 호환이 가능하게 된다.

보통은 이렇게까지 하면서 접근 불가를 선언해둔 곳에 접근해야 할 이유는 없을 것이다. 하지만 인생이 그리 쉬운 것은 아니지 않은가. 먹고 살자면 이렇게 해야할 이유도 있을 것이다. (남 이야기는 아니다. 내가 필요해서 우물을 판 것이다)