[C++] 클래스
C++은 파이썬과 마찬가지로 객체지향 프로그래밍을 지원하기 위해서 클래스가 제공된다.
클래스의 4가지 특징
클래스의 4가지 특징으로 캡슐화, 은닉화, 상속성, 다형성이 있다.
이 중 은닉화에서도 3가지 형태를 접할 수 있다.
- public
- private
- protected
C++에서 클래스 사용하기
우선 예시모형을 제시하면 아래와 같다.
class CPlayer
{
기본적으로 클래스의 변수는 private 상태이기 때문에 main함수 같은 외부 위치에서 접근이 불가능하다.
private:
char m_Name[32];
int m_Attack;
int m_Armor;
int m_Array[10];
public:
void change_Armor()
{
// 클래스 내부
m_Armor = 2020;
}
void change_Attack()
{
// 클래스 내부
m_Attack = 2021;
}
void output_Attack()
{
// 클래스 내부
std::cout << "공격력 : " << m_Attack << std::endl;
}
void output_Armor()
{
// 클래스 내부
std::cout << "방어력 : " << m_Armor << std::endl;
}
//아래가 기본 생성자이다.
/*
생성자도 함수이므로 인자가 있는 다양한 생성자를 만들어서 사용할 수 있다.
즉, 오버로딩이 가능하다.
*/
CPlayer():
// Initializer
m_Armor(0), m_Name{}
{
// Initializer : 변수를 선언과 동시에 초기화 해주는 역할을 한다.
m_Attack = 0;
std::cout << "CPlayer 생성자입니다." << std::endl;
}
CPlayer(int Attack, int Armor):
m_Attack(Attack),
m_Armor(Armor)
{
}
CPlayer(const char* Name) :
m_Attack(0),
m_Armor(0)
{
// 문자열 복사를 하더나 메모리 복사를 하면 된다.
strcpy_s(m_Name, Name);
memset(m_Array, 0, sizeof(int) * 10);
}
// 그리고 아래가 기본 소멸자이다.
// 소멸자는 무조건 이 형태 하나밖에 없다.
~CPlayer()
{
std::cout << "CPlayer 소멸자입니다." << std::endl;
}
};
어떻게 보면 구조체와 모양이 비슷하다.
그럼 구조체와 클래스의 차이는 무엇일까?
// 구조체 모형
struct FPlayer
{
char Name[32];
int Attack;
int Armor;
void output()
{
// 클래스 내부
std::cout << "output" << std::endl;
}
void change_Armor()
{
// 클래스 내부
Armor = 2020;
}
};
결정적 차이는 구조체는 기본적으로 변수가 public로 설정되어 있지만,
클래스는 기본적으로 변수가 private로 설정되어 있기 때문에 클래스 내부에서 선언된 함수로만 변수를 조작할 수 있다.
클래스의 생성자와 소멸자.
- 생성자 : 클래스를 이용해서 객체를 생성할 때 자동으로 호출되는 함수이다.
- 소멸자 : 클래스를 이용해서 생성한 객체가 메모리에서 해제될 때 자동으로 호출되는 함수이다.
생성자와 소멸자를 따로 만들어줄 수 있고, 따로 없을 경우 디폴드 생성자와 소멸자가 호출이 된다.
생성자와 소멸자는 함수 이름이 클래스의 이름과 정확하게 일치한다.
생성자와 소멸자는 반환타입이 아예 없다. (void 도 아니다.)
생성자에서는 보통 객체의 초기화 작업을 할 때 사용한다.
소멸자에서는 객체의 정리작업을 할 때 사용한다.
클래스의 복사생성자와 얕은 복사와 깊은 복사
인자를 const 클래스 타입 & 로 갖는 생성자를 말한다.
복사생성자는 클래스를 이용해서 객체를 생성할때 다른 객체의 값을 그대로 복사해서 생성되는 객체를 만들 때 사용한다.
게임에서는 주로 아이템이나 몬스터 같은 클래스에서 많이 활용을 할 수 있다.
아이템의 경우 몬스터를 잡아서 땅에 떨어질 때 원본 아이템의 정보를 복사해서 생성한 객체를 떨어뜨리면 편하게 작업할 수 있기 때문이다.
// 복사 예시 코드
class CItem
{
public:
CItem(const CItem& item)
{
strcpy_s(m_Name, item.m_Name);
m_Attack = item.m_Attack;
m_Price = item.m_Price;
m_Array = new int[10];
memcpy(m_Array, item.m_Array, sizeof(int) * 10);
}
public:
int m_Attack = 0;
int m_Price = 0;
char m_Name[32] = {};
int* m_Array = {};
}
int main()
{
// 상점이 가지고 있는 원본 아이템
CItem or_item;
// 아이템 구매시 상점의 원본 아이템을 복제해서 만들어낸다.
// 갠적인 느낌으로 파이썬의 self에 대응되는 것 같다.
CItem item(or_item);
std::cout << "이름 : " << item.m_Name << std::endl;
std::cout << "공격력 : " << item.m_Attack << std::endl;
std::cout << "가격 : " << item.m_Price << std::endl;
}
복사는 두 가지 방법이 있는데, 얕은 복사와 깊은 복사가 있다.
얕은 복사
위 코드에서 다음에 해당한다.
strcpy_s(m_Name, item.m_Name);
m_Attack = item.m_Attack;
m_Price = item.m_Price;
단순히 원래 객체 구성에서 값만을 복사해서 새로운 객체를 만들어내는 복사를 얕은 복사라고 한다.
예를 들어서 위의 코드를 통해서 or_item객체가 가지고 있는 값을 복사해서 item이라는 값을 만들어내는 것이다.
깊은 복사
위 코드에서 다음에 해당한다.
m_Array = new int[10];
memcpy(m_Array, item.m_Array, sizeof(int) * 10);
배열이나 포인터를 복사해야 할 때는 주소값을 가져와버리는데,
두 객체에 포함되어 있는 메모리 주소값이 같아버리면 소멸자를 통해서 메모리를 해제할 때 모두 같이 값이 사라져버리는 현상이 발생한다.
따라서, 메모리의 주소를 가져오는 것이 아니라, 새로운 메모리를 마련해서 값을 새로운 메모리 안에다가 복사하는 작업을 해야한다. 이런 원리의 복사를 깊은 복사라고 한다.