객체지향의 시작

절차(procedural)지향 프로그래밍 : 함수가 메인
객체(object)지향 프로그래밍 : 객체가 메인, 객체에는 데이터와 데이터를 조작하는 동작이 모두 포함됨

class Knight
{
	public:  // 멤버 함수
		void Move(int y, int x);
		void Attack();
		void Die() { _hp = 0; cout << "Die" << endl; }
	public:  // 멤버 변수
		int _hp;
		int _attack;
		int _posY;
		int _posX;
};

void Knight::Move(int y, int x)
{
	_posY = y;
	_posX = x;
	cout << "Move" << endl;
}

void Knight::Attack()
{
	cout << "Attack : " << _attack << endl;
}

int main()
{
	Knight k1;
	k1._hp = 100;
	k1._attack = 10;
	k1._posY = 0;
	k1._posX = 0;

	Knight k2;
	k2._hp = 80;
	k2._attack = 5;
	k2._posY = 1;
	k2._posX = 1;

	k1.Move(2, 2);
	k1.Attack();
	k1.Die();

	return 0;
}

class는 일종의 설계도 역할을 함
객체의 크기는 멤버 변수 데이터 크기의 합이 됨


생성자와 소멸자 #1

생성자(constructor) : 클래스의 시작을 알리는 함수, 여러개 존재 가능 소멸자(destructor) : 클래스의 끝을 알리는 함수, 하나만 존재

class Knight
{
	public:
		Knight() { cout << "기본 생성자 호출" << endl; };
		Knight(const Knight& knight)
		{
			_hp = knight._hp;
			_attack = knight._attack;
			_posX = knight._posX;
			_posY = knight._posY;
		}
		Knight(int hp)
		{
			_hp = hp;
			_attack = 10;
			_posX = 0;
			_posY = 0;
		}
		~Knight() { cout << "소멸자 호출" << endl; };
		...
}

Knight() : 기본 생성자, 인자 없음

  • 생성자를 명시하지 않은 경우, 기본 생성자가 컴파일러에 의해 자동으로 만들어져 암시적(implicit) 생성자로 사용됨
  • 생성자를 명시적(explicit)으로 선언한 경우 기본 생성자는 생성되지 않음
  • 기본 생성자 외에도 함수의 오버로딩을 사용하여 필요에 따라 여러 종류의 생성자를 설정할 수 있음

Knight(const Knight& knight) : 복사 생성자, 동일한 타입의 다른 객체를 이용하여 초기화할 때 사용

  • 복사 생성자를 명시하지 않은 경우 암시적 복사 생성자가 사용됨
  • 암시적 복사 생성자는 객체의 모든 멤버를 동일하게 복사함

~Knight() : 소멸자


생성자와 소멸자 #2

Knight k2(k1); Kngiht k3 = k1;은 복사 생성자가 사용됨
Knight k4; k4 = k1;k4를 생성자를 사용하여 생성한 후 k1을 대입함

Knight(int hp)와 같이 인자를 1개만 받는 생성자를 타입 변환 생성자라고 부르기도 함

  • 타입 변환 생성자가 존재하는 경우 암시적 형변환이 문제가 될 수 있음
  • 따라서 명시적인 용도로만 사용하도록 생성자 앞에 explicit 키워드를 붙여 활용할 수 있음

상속성

클래스는 상속성을 가지기 때문에 유용하게 재사용할 수 있음

class Player
{
	public:
		Player()
		{
			_hp = 0;
			_attack = 0;
			_defence = 0;
			cout << "Player 기본 생성자 호출" << endl; 
		}
		~Player() { cout << "Player 소멸자 호출" << endl; }
		void Move() { cout << "Player Move" << endl;}
		void Attack() { cout << "Player Attack" << endl; }
		void Die() { cout << "Player Die" << endl;}
	public:
		int _hp;
		int _attack;
		int _defence;
};

class Knight : public Player
{
	public:
		Knight()
		// 선처리 영역, 여기서 부모의 생성자를 호출
		{
			_stamina = 100;
			 cout << "Knight 기본 생성자 호출" << endl; 
		}
		Knight(int stamina) : Player(100)
		{
			_stamina = stamina;
			cout << "Knight(int stamina) 생성자 호출" << endl;
		}
		~Knight() { cout << "Knight 소멸자 호출" << endl; }
		int _stamina;
}

class Mage : public Player
{
	public:
		int _mp;
}

int main()
{
	Knight k;
	k._hp = 10;
	k.Attack();
}

부모 클래스에 정의되어있는 함수를 재정의할 수 있음
자식 클래스에도 생성자가 있어야하며, 부모의 생성자 먼저 호출 후 자식의 생성자가 호출됨

  • 정확히는 자식 클래스 생성자의 선처리 영역에서 부모의 생성자가 먼저 호출된 후 자식 클래스 생성자의 구문들을 처리함
  • 소멸자는 생성자의 역순으로 호출됨

Kngiht(int stamina) : Player(100)과 같이 사용하여 부모의 생성자를 선택할 수 있음