들어가며
전 포스트까지는 C++의 구조체 클래스의 일종이었다.
이제부터는 진짜 클래스를 배울 수 있다.
드디어!
와!
구조체와 클래스의 차이점, 단 하나!
키워드 『struct』대신 『class』를 사용하면,
구조체가 아닌 클래스가 선언된다.
즉, 다음의 코드는 클래스의 정의라고 할 수 있다.
class Weapon
{
char weaponID[WEAPON_CONST::ID_len]; // 무기 아이디
int dura; // 내구도
int dura_dbf; // 내구도 디버프 조건
int atk_dmg; // 공격력
void WEAPON::ShowWeaponState()
{ · · · · · }
void WEAPON::Attack()
{ · · · · · }
void WEAPON::Repair()
{ · · · · · }
};
'도저히 다른 게 뭔지 모르겠는 뎁쇼.'
그대의 극악으로 좁은 시야각에 조의를 표한다.
유감을 표하는 것이 아닌지 의문이 드는가?
음? 시야가 죽은 거 아니었는가?
뭐, 왜.
아무튼 구조체에서 달라진 점은 딱 하나다.
위에서도 말했다시피, 키워드 『struct』가
무려 『class』로 바뀌었다(!).
근데 이렇게 키워드를 바꿔놓으면
앞서 코드에서 보여주었던 변수 선언을
하지 못하게 되는 불상사가 발생한다.
// 이 변수 선언은 불가능하다.
Weapon sword_R = { "sword_R", WEAPON_CONST::MAX_DURABILITY, false, 8 };
//WEAPON_CONST::MAX_DURABILITY = 250
이유가 무엇인가.
왜냐하면 클래스 내에 선언된 함수가 아닌,
다른 영역에서 변수를 초기화하려 했기 때문이다.
클래스는 기본적으로 (별도의 선언을 하지 않으면)
클래스 내에 선언된 함수에서만 클래스 내에 선언된 변수에
접근이 가능하다.
따라서 다음과 같은 형태로 클래스 변수를 선언해야 한다.
Weapon sword_R;
'개 귀찮네, 아니 그럼 초기화는 어케하누;;'
너무 성급하지 않길 바란다.
클래스 변수의 초기화는 다음과 같이 하면
int main()
{
Weapon sowrd_R; // 변수 선언 후
sword.atk_dmg = 8; // 초
sword.dura_dbf = false; // 기
sword.dura = WEAPON_CONST::MAX_DURABILITY; // 화
· · · · ·
return 0;
}
컴파일 오류가 발생하니까
이렇게 하지 말길 바란다.
방금 전에 말했다시피,
클래스 내에 선언된 변수는
클래스 내에 선언된 함수에서만
접근이 가능하다.
'하······, 접근도 못 하는 거 어디다가 써먹누······.'
물론 접근을 못 하면 이미 먼저 길을 닦아놓으신,
선대 프로그래머 분들이 과거에 해치워버렸을 것이다.
그러나 클래스는 멤버의 접근과 관련해서
다음과 같이 말한다.
'접근이랑 관련해서 별도로 선언 안 하면, 클래스 내부에 선언된 변수나 함수에 대한
접근을 허용하지 않을 거니까, 접근이랑 관련된 지시를 별도로 하셈 ㅇㅇ.'
이렇듯, 클래스는 정의를 하는 과정에서
각각의 변수 및 함수의 접근 허용 범위를
별도로 선언해야 한다.
그리고 바로 이것이 키워드 『struct』를 사용한 구조체와
키워드 『class』를 사용한 클래스의 차이점이다.
사실 조금 부정확하게 설명된 부분이 없잖아 있다.
이후 『접근 제어 지시자』에서 보다 정확하게 설명하겠다.
삼도수군통제사 - 접근 제어 지시자
C++의 접근 제어 지시자는 총 3가지가 있다.
- 『public』
// 어디서든 접근 허용 - 『protected』
// 상속 관계일 경우, 유도 클래스에서의 접근 허용 - 『private』
// 클래스 내부에서만 접근 허용
이 중에서 『protected』는 『상속』과 관련이 있으므로
아무튼 다음에 알아보도록 하고,
지금은 『public』과 『private』에 대해서 알아보도록 하자.
그럼 다음의 코드를 해석해보자.
#include <iostream>
#include <cstring>
using namespace std;
namespace WEAPON_CONST
{
enum
{
ID_LEN = 30, // 이름 길이
DURA_MAX = 250, // 최대 내구도
DURA_DBUF = 100, // 내구도 디버프 수준
DURA_STEP = 2 // 내구도 감소 수준
};
}
class Weapon
{
private:
// 무기 이름
char weaponID[WEAPON_CONST::ID_LEN];
int dura_gauge; // 내구도 수준
int dura_dbuf; // 내구도 디버프 판단
int atk_CurPwr; // 현재 공격력
int atk_OrgPwr; // 기본 공격력
public:
// 클래스 변수 초기화 함수
void InitMembers(const char* ID, int dura, int power);
// 무기 상태 표시 함수
void ShowWeaponState();
// 무기 사용 함수
void Attack();
// 무기 수리 함수
void Repair();
};
void Weapon::InitMembers(const char* ID, int dura, int power)
{
strcpy_s(weaponID, ID); // 무기 이름
dura_gauge = dura; // 최대 내구도
dura_dbuf = false; // 내구도 디버프 비활성화
atk_CurPwr = power; // 현재 공격력
atk_OrgPwr = power; // 기본(현재) 공격력
}
void Weapon::ShowWeaponState()
{
cout << "무기 이름: " << weaponID << endl;
cout << "무기 내구도: " << WEAPON_CONST::DURA_MAX << "/" << dura_gauge << endl;
cout << "무기 공격력: " << atk_CurPwr << endl << endl;
}
void Weapon::Attack()
{
dura_gauge -= WEAPON_CONST::DURA_STEP; // 무기 사용으로 내구도 감소
if (dura_gauge <= 0) // 내구도 0이하
{
atk_CurPwr = 0; // 무기 공격력 0 + 부서짐
cout << "The " << weaponID << " is broken." << endl << endl;
return;
}
else if (dura_dbuf == false && dura_gauge <= WEAPON_CONST::DURA_DBUF)
{ // 해당 무기가 디버프 비활성화 + 내구도가 디버프 내구도 이하라면
dura_dbuf = true; // 디버프 활성화
atk_CurPwr = atk_CurPwr / 2; // 공격력 절반 감소(디버프)
}
}
void Weapon::Repair()
{
if (dura_gauge == WEAPON_CONST::DURA_MAX)
{ // 내구도 게이지가 가득찼다면
cout << "The " << weaponID << " does not need to be repaired." << endl << endl;
return;
}
cout << "The " << weaponID << " is repaired." << endl << endl;
dura_dbuf = false; // 다시 내구도 디버프 비활성화
dura_gauge = WEAPON_CONST::DURA_MAX; // 최대 내구도 회복
atk_CurPwr = atk_OrgPwr; // 무기 현재 공격력 회복
}
int main()
{
Weapon sword_R;
sword_R.InitMembers("Sword_R", WEAPON_CONST::DURA_MAX, 8);
sword_R.ShowWeaponState();
for (int i = 0; i < 100; i++)
{
if (i == 99)
{
cout << "Attack " << i+1 << " times." << endl << endl;
}
sword_R.Attack();
}
sword_R.ShowWeaponState();
for (int i = 0; i < 25; i++)
sword_R.Attack();
sword_R.ShowWeaponState();
sword_R.Repair();
sword_R.ShowWeaponState();
sword_R.Repair();
return 0;
}
전 글에서 계속 사용하던 무기 관련 코드를
클래스도 적용 시킬 겸, 보수도 하였다.
'접근 머시깽이 뒤에는 왜
세미콜론(;)이 아니라 콜론(:)이 붙는겨?'
왜냐하면, 『접근 제어 지시자』가 특정 위치 정보를 알리는
『레이블(Label)』이라는 녀석이기 때문이다.
우리가 알고 있는 녀석들 중에서도 콜론을 사용하는 녀석이 있는데,
『switch』문에 사용되는 『case』도 『레이블』이기 때문에
콜론을 사용한다.
위 코드를 통해서, 그리고 지금까지 설명한 내용을 종합하면
다음과 같다.
- 『접근 제어 지시자 A』가 선언되면, 그 이후에 등장하는 변수나 함수는
A에 해당하는 범위 내에서만 접근이 가능하다. - 새로운 『접근 제어 지시자 B』가 선언되면, 그 이후로 등장하는 변수나 함수는
B에 해당하는 범위 내에서만 접근이 가능하다. - 키워드 『struct』를 이용해서 정의한 구조체(혹은 클래스)에 선언된 변수와 함수에
별도의 접근 제어 지시자를 선언하지 않으면, 모든 변수와 함수는 『public』으로 선언된다. - 키워드 『class』를 이용해서 정의한 클래스에 선언된 변수와 함수에
별도의 접근 제어 지시자를 선언하지 않으면, 모든 변수와 함수는 『private』로 선언된다.
그리고 위에서 설명하는 『struct』와 『class』의 선언에 따른 차이가
『구조체』와 『클래스』의 유일한 차이점이다.
즉, 『구조체』도 『클래스』도 접근 제어 지시자의 선언이 가능하고,
그 의미도 동일하다.
다만, 『접근 제어 지시자』를 선언하지 않았을 때,
『구조체』는 『public』으로, 『클래스』는 『private』로 선언할 뿐이다.
'않이 왜 굳이 『private』로 선언해서 접근하기 불편하게 만듦?;;
그냥 싹다 『public』으로 선언하면 편하잖수······.'
근데 사실 본인도 위의 코드를 만들 때 똑같은 생각을 했다.
그래도 분명 뭔가 이유가 있으니까 저 고생을 사서 하는 것이 아닐까
하는 킹리적 갓심이 든다.
마치며
아직 구조체가 익숙한 본인을 포함한 여러 독자들도
클래스가 좀 거북하다.
아무튼 다음 글에서 그 점을 해소할 것이다.
참고 및 출처
|