본문 바로가기

프로그래밍/C++

[C++] C의 구조체와는 클라스가 다르다, 클래스(Class) - 2

들어가며

오늘도 클래스를 제대로 배울 수 있는지

클래스를 배우고 있는지는 아무도 모른다.

아무튼 일단 들이받고 보는 거다.

하나 정확한 것은, 클래스로 나아가고 있다는 점.

절대 클래스를 배움에 있어서 빗나가고 있지는 않다.

아무튼 시작하겠다.

 

구조체 안에 enum 상수의 선언

전에 필자가 보여준 코드에는

다음과 같은 매크로 상수들이 있다.

#define ID_len                  30     // 아이템 이름(ID)
#define MAX_DURABILITY          250    // 내구도 최대치
#define DURABILITY_DEBUFF       50     // 내구도 디버프
#define DURABILITY_STEP         2      // 내구도 감소 수치

 

그런데 이들 상수 역시 구조체 『WEAPON』에게만

의미가 있는 상수들이다.

즉, 다른 영역에서 사용하도록 정의된 상수가 아니다.

따라서 이 매크로 상수들도 구조체 내에 포함시키는 것이

효율적이라는 것을 합리적으로 알 수 있다.

 

그러므로 이러한 경우에는 열거형 『enum을 이용해서

다음과 같이 구조체 내에서만 유효한 상수를 정의하면 된다.

struct WEAPON
{
    enum                                 // 구조체 내부에 열거형 삽입
    {
        ID_len =                 30,     // 아이템 이름(ID)
        MAX_DURABILITY =         250,    // 내구도 최대치
        DURABILITY_DEBUFF =      50,     // 내구도 디버프
        DURABILITY_STEP =        2,      // 내구도 감소 수치
    };
    
   char weaponID[ID_len];  // 무기 아이디
   int dura;               // 내구도
   int dura_dbf;           // 내구도 디버프 조건
   int atk_dmg;            // 공격력

    void ShowWeaponState()
    { · · · · · }
    
    void Attack()
    { · · · · · }
    
    void Repair()
    { · · · · · }
};

(열거형에 대해서 잘 모른다면 위의 『enum』을 눌러보면 신기한 일이 벌어진다.)

열거형을 구조체 내부에 삽입하는 것이 마땅치 않다면?

그럼네임 스페이스』를 이용해서

상수가 사용되는 영역을 명시하는 것도 방법이다.

 

그리고 이렇게 네임 스페이스를 이용하면,

몇몇 구조체들 사이에서만 사용하는

상수들을 선언할 때 도움이 되며,

마냥 때려 박는 것보다 더 나은 가독성을 갖게 된다.

 

이러한 사실을 배웠다면?

 

이름 공간 안에 구조체 안에 enum 상수 선언

당연히!

사용해보는 것이 인지상정 아니겠는가.

그 코드는 다음과 같다.

 

읽을 때의 포인트는 이전의 매크로 상수(#define)들이

『WEAPON_CONST::(열거형)』으로 대체된 것이다.

#include <iostream>

using namespace std;

namespace WEAPON_CONST
{
    enum
    {
        ID_len =                 30,     // 아이템 이름(ID)
        MAX_DURABILITY =         250,    // 내구도 최대치
        DURABILITY_DEBUFF =      50,     // 내구도 디버프
        DURABILITY_STEP =        2       // 내구도 감소 수치
    };
}

struct WEAPON
{
    char weaponID[WEAPON_CONST::ID_len];    // 무기 아이디
    int dura;                               // 내구도
    int dura_dbf;                           // 내구도 디버프 조건
    int atk_dmg;                            // 공격력

    void ShowWeaponState()
    {
        cout << "Name: " << weaponID << endl;
        cout << "Durability: " << "250/" << dura << endl;
        cout << "Attack Damage: " << atk_dmg << endl << endl;
    }

    void Attack()
    {
        // 내구도가 0이면 공격력 0
        if (dura <= 0)
        {
            cout << "The " << weaponID << " is broken." << endl << endl;
            atk_dmg = 0;
            return;
        }
        // 일정 수준 이하의 내구도는 공격력 감소 디버프
        else if (dura_dbf == false && dura <= WEAPON_CONST::MAX_DURABILITY)
        {
            atk_dmg = (atk_dmg / 2);
            dura_dbf = true;
        }

        dura -= WEAPON_CONST::DURABILITY_STEP;
    }

    void Repair()
    {
        if (dura == WEAPON_CONST::MAX_DURABILITY)
            return;
        else if (dura < WEAPON_CONST::MAX_DURABILITY)
        {
            // 수리하면 내구도와 공격력 복구(디버프 해제)
            if (dura_dbf == true)
            {
                atk_dmg = atk_dmg * 2;
                dura_dbf = false;
            }
            dura = WEAPON_CONST::MAX_DURABILITY;
        }
    }

};

int main()
{
    WEAPON sword = { "sword", WEAPON_CONST::MAX_DURABILITY, false, 8 };
    · · · · · 
    return 0;
}

 

구조체 내에 함수를 정의했을 뿐인데

뭔가 상당히 느낌이 달라졌다.

분명 좋긴 한데,

구조체의 부피가 너무 커져버렸다.

 

함수는 외부로 빼돌릴 수 있다

함수가 포함되어 있는 C++ 구조체를 봤을 때,

다음의 정보들이 쉽게 눈에 들어와야 코드의 분석이 용이하다.

  • 선언되어 있는 변수 정보
  • 정의되어 있는 변수 정보
보통 프로그램을 분석할 때,

흐름 및 골격 위주로 분석하는 경우가 많다.

그리고 이러한 경우에는 함수의 기능만 파악하지

함수의 세부적인 구변까지는 신경 쓰지 않는다.

따라서 구조체를 보는 순간,

정의되어 있는 함수의 종류와 기능

한눈에 들어오게끔 코드를 작성하는 것이 좋다.

 

그러므로 구조체 내에 정의된 함수가

많거나 길이가 길다면, 다음과 같이

함수 밖으로 빼돌리도록 하자.

namespace WEAPON_CONST
{
    enum
    { · · · · · };
}


struct WEAPON
{
    · · · · ·
    void ShowWeaponState();
    void Attack();
    void Repair();
    · · · · ·
};

void ShowWeaponState()
{ · · · · · }
    
void Attack()
{ · · · · · }
    
void Repair()
{ · · · · · }

 

보면 알 수 있듯이,

구조체 내부함수의 원형 선언을 하고

함수의 정의구조체의 외부로 빼내는 것이다.

이때 빼낸 다음에 해당 함수가 어디에

정의되어 있는지에 대한 정보만 추가해주면 된다.

다음처럼 말이다.

namespace WEAPON_CONST
{
    enum
    { · · · · · };
}


struct WEAPON
{
    · · · · ·
    void ShowWeaponState();
    void Attack();
    void Repair();
    · · · · ·
};

void WEAPON::ShowWeaponState()
{ · · · · · }
    
void WEAPON::Attack()
{ · · · · · }
    
void WEAPON::Repair()
{ · · · · · }

 

그리고 딱히 언급하지는 않았지만,

사실 구조체 안에 함수가 정의되어 있으면,

함수인라인으로 처리하라는 의미가 내포된다.

즉, 다른 함수(메인 함수)에서 구조체 함수를 호출했을 때

해당 호출문에 해당 구조체 함수를 인라인화 한다는 말이다.

 

하지만 위의 코드처럼

함수의 정의를 구조체 밖으로 빼내면,

이러한 의미가 사라지게 된다.

즉, 이제 어디든 간에 인라인화를 하지 않고

그냥 호출해서 사용한다는 소리다.

 

따라서 인라인의 의미를 그대로 유지하기 위해서는

키워드 『inline』을 명시하여 인라인 처리를 지시해야 한다.

namespace WEAPON_CONST
{
    enum
    { · · · · · };
}

struct WEAPON
{
    · · · · ·
    void ShowWeaponState();
    void Attack();
    void Repair();
    · · · · ·
};

inline void WEAPON::ShowWeaponState()
{ · · · · · }
    
inline void WEAPON::Attack()
{ · · · · · }
    
inline void WEAPON::Repair()
{ · · · · · }

이렇게 한다면 인라인화가 된다.

그림 5 - 난 그런거 모올라요오

'그래서 인라인이 뭔데 씹덕아;'

라고 생각하는 독자들을 위해서

필자는 이미 인라인이 무엇인가에 대해

글을 쓴 적이 있으니, 잘 돌아다녀보길 바란다.

 

마치며

자, 이제 C++에서의 구조체에 대한 설명이 일단락 되었다.

?

뭐지,

누누히 말하지만 나는 책을 보고 공부하는데

얘가 클래스를 안 알려준다.

 

그런데 알고 보니까 지금까지 배워온 것들이

모두 C++의 클래스의 일종으로 간주되는 것이었다.

그래서 구조체 내부에 함수를 정의할 수 있었던 것이다.

 

사실 지금까지 배운 것도 클래스라고 표현해도 된다고 한다.

 

아ㅋㅋ


참고 및 출처

윤성우의 열혈 C++ 프로그래밍
국내도서
저자 : 윤성우
출판 : 오렌지미디어 2010.05.12
상세보기