[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);

배열이나 포인터를 복사해야 할 때는 주소값을 가져와버리는데,
두 객체에 포함되어 있는 메모리 주소값이 같아버리면 소멸자를 통해서 메모리를 해제할 때 모두 같이 값이 사라져버리는 현상이 발생한다.

따라서, 메모리의 주소를 가져오는 것이 아니라, 새로운 메모리를 마련해서 값을 새로운 메모리 안에다가 복사하는 작업을 해야한다. 이런 원리의 복사를 깊은 복사라고 한다.


© 2022.07. by Wookey_Kim

Powered by Hydejack v7.5.2