[TIL] 23-12-28

Lev·2023년 12월 28일
0

📓TIL Archive

목록 보기
13/33

클래스 Class (이어서)

상속 Inheretance (이어서)

부모의 함수를 사용하고 싶다면, 풀네임을 사용하는 것이 정석 (이지만 사실 필수는 아니다.)
부모가 자식보다 먼저 호출된다 ⇒ 그래야 자식이 부모의 멤버변수나 멤버함수를 사용할 수 있다.

#include <iostream>

class FightUnit
{
public:
	FightUnit()
	{
		printf_s("FightUnit 디폴트 생성자");
	}

	FightUnit(int Value)
	{
		printf_s("FightUnit Int 생성자");
	}

	int Att;
};

class Player0 : public FightUnit
{
public:
	Player0()
		: FightUnit()
	{
		printf_s("Player0 생성자");
	}
};

class Player1 : public FightUnit
{
public:
	Player1()
		: FightUnit(10)
	{
		printf_s("Player1 생성자");
	}
};

int main()
{
	Player0 NewPlayer0 = Player0();	// FightUnit 디폴트 생성자Player0 생성자
	Player1 NewPlayer1 = Player1();	// FightUnit Int 생성자Player1 생성자
}

다중 상속 Multiple Inheretance

class MoveUnit
{
public:
	void Move()
	{

	}
};

class TalkUnit
{
public:
	void Talk(const TalkUnit& _Unit)
	{

	}
};

class FightUnit
{
public:
	void Fight(const FightUnit& _Unit)
	{
		// 스스로와 Fight하는 상황에 대한 디펜스코드
		if (this == &_Unit)
		{
			return;
		}
	}
};

class Player : public MoveUnit, public TalkUnit, public FightUnit	// [다중 상속]
{

};

class Monster : public MoveUnit, public FightUnit	// [다중 상속]
{

};

int main()
{
	Player NewPlayer1;
	Player NewPlayer2;
	Monster NewMonster;

	NewPlayer1.Talk(NewPlayer2);
	// NewPlayer1.Talk(NewMonster); => Monster가 TalkUnit을 상속받지 않아 불가능
}

업캐스팅 Upcasting

자식클래스의 객체는 언제든 부모클래스의 참조형이 될 수 있다!

굉장히 권장되는 암시적 형변환이다.

class Item
{
public:
	int Cost;
};

class Sword : public Item
{

};

// 근본적인 개념을 제공하기 위한 클래스 => 부모
class FightUnit
{
public:
	int PAtt;
	int EAtt;
	int MAtt;
	int Hp;
	int Def;
	int Cri;

	// void Damage(int _Att, int _Cri/*, ...*/)
	// 원래대로면 위와 같이 많은 인자를 받아야 하는 함수지만
	// 아래와 같이 구현하면 FightUnit의 모든 멤버들에 접근할 수 있다
	void Damage(const FightUnit& _AttUnit)
	{
		Hp -= _AttUnit.PAtt - Def * _AttUnit.EAtt;
	}
};

// 클래스를 상속받으면 [Is A]
class Player : public FightUnit	// Player 'Is A' FightUnit
{
private:
	// 클래스를 멤버로 가지고 있으면 [Has A]
	Sword Weapon;	// Player 'Has A' Sword
};

class Monster : public FightUnit
{
	// FightUnit을 상속받는 것만으로도 싸울 수 있게 된다
};

class NPC : public FightUnit
{
	// FightUnit을 상속받는 것만으로도 싸울 수 있게 된다
};

int main()
{
	{
		Player NewPlayer;
		FightUnit Unit = NewPlayer;

		// int A;
		// bool C = A;
	}
	{
		// [업캐스팅]
		Player NewPlayer;
		FightUnit* Unit = &NewPlayer;

		// int A;
		// bool* Ptr = reinterpret_cast<bool*>(&A);
	}

	Player NewPlayer;
	Monster NewMonster;

	NewPlayer.Damage(NewMonster);
	NewMonster.Damage(NewPlayer);
}

다운캐스팅 Downcasting

부모클래스의 참조형에서 자식클래스의 참조형으로 형변환하는 것

건강하지 못한 캐스팅이라 권장되지 않는다...
구분방법을 마련해둘 수 있긴 하지만, 인간인지라 언제든 실수할 여지가 있기도 하다.
그래도 절대 사용하지 않는건 아니다.

class FightUnit
{
public:
	int Type = -1;	// Player = 0, Monster = 1
	int Att = 20;
	int Hp = 10;
};

class Player : public FightUnit
{
public:
	Player()
	{
		Type = 0;	// 구분방법을 마련해 두는 것...
	}

	int Level = 0;
};

class Monster : public FightUnit
{

};

int main()
{
	// [업캐스팅] => 자식클래스는 언제든 부모클래스의 참조형으로 형변환 될 수 있다
	Monster NewMonster;
	FightUnit* NewUnit = &NewMonster;

	// [다운캐스팅] => 부모클래스의 참조형을 자식클래스의 참조형으로 형변환한다
	Player* CurPlayer = reinterpret_cast<Player*>(NewUnit);
	// Player에는 존재하지만 Monster에는 존재하지 않는 Level 변수가 이상한 값이 된다
	// 어딘지 알 수 없는 메모리를 사용하게 된다
}

상수멤버함수 Const Member Function

class FightUnit
{
public:
	Player* Test;
	// 절대 하지 말자!
	// 부모클래스가 자식클래스의 자료형을 사용하는 것은 절대 금기
};

class Player : FightUnit
{
public:
	// 함수 뒤에 const를 붙이는 것은
	// this를 const Player* const this 로 변환하겠다는 의미
	int GetHp() const	// [상수멤버함수]
		// const Player* const this
		// Player* const this
		// GetHp 함수의 인자가 위에서 아래로 변한다
	{
		// Hp = 300; => 이렇게 값을 변경하는게 불가능해진다
		return Hp;
	}
	// Get 함수에 const를 붙이는 것은
	// 값을 가져왔을때 값이 변하지 않을 것이라고 보장하는 것

private:
	int Hp;
};

Console Game (생성자 상속 ver.)

// [ConsoleObject.h]
#pragma once
#include "Math.h"

class ConsoleObject
{
public:
	ConsoleObject();

	ConsoleObject(const int2& _StartPos, char _RenderChar);	// (3)

	inline int2 GetPos()
	{
		return Pos;
	}

	inline char GetRenderChar()
	{
		return RenderChar;
	}

	void SetPos(const int2& _Pos);	// (2)

protected:
	int2 Pos = { 0, 0 };
	char RenderChar = '@';
};
// [ConsoleObject.cpp]
#include "ConsoleObject.h"

ConsoleObject::ConsoleObject()
{

}

ConsoleObject::ConsoleObject(const int2& _StartPos, char _RenderChar)
	: Pos(_StartPos), RenderChar(_RenderChar)
{

}

void ConsoleObject::SetPos(const int2& _Pos)	// (2)
{
	Pos = _Pos;
}
// [Player.cpp]
#include <conio.h>
#include "Player.h"
#include "ConsoleScreen.h"

Player::Player()
{

}

Player::Player(const int2& _StartPos, char _RenderChar)
	: ConsoleObject(_StartPos, _RenderChar)	// (3)
{
	/*
	(1)
	Pos = _StartPos;
	RenderChar = _RenderChar;
	*/
}

(1) 초기화 대신 대입해주는 방법
(2) Set 함수를 이용해 자식클래스가 부모클래스의 멤버변수에 접근할 수 있도록 하는 방법
(3) 부모클래스의 생성자를 이용해 자식클래스를 초기화하는 방법

Text RPG (객체 지향 ver.)

// [TextRPG.cpp]
#include <iostream>
#include "Player.h"
#include "FightZone.h"
// #include	<stdlib.h>

/*
과제
(1) SetName 함수 만들기
(2) 랜덤값으로 데미지 주기
(3) 선공 후공을 랜덤으로 결정하기
*/

int main()
{
	// (2), (3)
	// int Value = 0;
	// __int64 Seed = reinterpret_cast<__int64>(&Value);
	// srand(static_cast<unsigned int>(Seed));

	Player NewPlayer = Player();	// 플레이어 생성

	NewPlayer.SetName("Player");	// 플레이어 이름 변경
	
	FightZone NewZone;	// 전투공간 생성

	NewZone.Fight(NewPlayer);	// 플레이어를 전투공간에 넣기
// [StatusUnit.h]
#pragma once

class StatusUnit
{
public:
	inline char* GetName()
	{
		return Name;
	}

	void SetName(const char* _Name);	// (1)

	void StatusRender();

protected:
	char Name[100] = "None";
	int Hp = 100;
	int MinAtt = 10;
	int MaxAtt = 20;
	int Speed = 20;
	int Gold = 0;
};
// [StatusUnit.cpp]
#include "StatusUnit.h"
#include <iostream>

void StatusUnit::SetName(const char* _Name)	// (1)
{
	int Cnt = 0;
	while (_Name[Cnt])
	{
		++Cnt;
	}

	for (int i = 0; i < Cnt + 1; i++)
	{
		Name[i] = _Name[i];
	}
}

void StatusUnit::StatusRender()
{
	int Size = printf_s("%s", Name);

	for (int i = 0; i < 50 - Size; i++)
	{
		int a = 0;
		printf_s("-");
	}

	printf_s("\n");
	printf_s("공격력 %d ~ %d\n", MinAtt, MaxAtt);
	printf_s("체력 %d\n", Hp);

	for (int i = 0; i < 50; i++)
	{
		printf_s("-");
	}

	printf_s("\n");
}
// [Player.h]
#pragma once
#include "FightUnit.h"

class Player : public FightUnit
{

};
// [Monster.h]
#pragma once
#include "FightUnit.h"

class Monster : public FightUnit
{

};
// [FightUnit.h]
#pragma once
#include "StatusUnit.h"

class FightUnit : public StatusUnit
{
public:
	// Get 함수

	inline int GetMinAtt()
	{
		return MinAtt;
	}

	inline int GetMaxAtt()
	{
		return MaxAtt;
	}

	inline bool IsDeath()
	{
		return Hp <= 0;
	}

	int GetRAtt() const;	// (2)

	int GetRSpeed() const;	// (3)

	// Fight용 함수

	void DamageLogic(const FightUnit& _Unit);

	void DamageRender();

protected:
	int CurAtt = 0;
};
// [FightUnit.cpp]
#include "FightUnit.h"
#include <iostream>
#include <random>
// #include	<stdlib.h>

int FightUnit::GetRAtt() const	// (2)
{
	std::random_device rd;
	std::mt19937 gen(rd());
	std::uniform_int_distribution<> dist(MinAtt, MaxAtt);

	return dist(gen);

	// int Damage = rand() % ((MaxAtt - MinAtt) + 1);
	// return MinAtt + Damage;
}

int FightUnit::GetRSpeed() const	// (3)
{
	std::random_device rd;
	std::mt19937 gen(rd());
	std::uniform_int_distribution<> dist(MinAtt, MaxAtt);

	return dist(gen);

	// return rand() % (Speed + 1);
}

void FightUnit::DamageLogic(const FightUnit& _Unit)
{
	CurAtt = _Unit.GetRAtt();
	Hp -= CurAtt;
}

void FightUnit::DamageRender()
{
	printf_s("%s가 %d만큼 피해를 받았습니다.\n", Name, CurAtt);
}
// [FightZone.h]
#pragma once
#include "Monster.h"
// Player까지 알 필요는 없다
// 객체 지향이 아니라 대상의 경우를 무수하게 나누는 코드가 되어버릴 수 있기 때문

class FightZone
{
public:
	FightZone();

	// 승패가 갈릴 때까지 전투하는 함수
	void Fight(FightUnit& _Player);

protected:
	Monster NewMonster = Monster();

	// 1회 합을 주고 받는 함수
	bool FightLogic(FightUnit& _First, FightUnit& _Last, FightUnit& _Top, FightUnit& _Bot);
	// First => 선공, Last => 후공, Top => 상단에 렌더, Bot => 하단에 렌더
};
// [FightZone.cpp]
#include "FightZone.h"
#include <conio.h>
#include <iostream>

FightZone::FightZone()
{
	NewMonster.SetName("Monster");
}

void FightZone::Fight(FightUnit& _Player)
{
	while (true)
	{
		_Player.StatusRender();
		NewMonster.StatusRender();

		bool IsEnd = false;

		if (_Player.GetRSpeed() >= NewMonster.GetRSpeed())
		{
			printf_s("%s의 선공\n", _Player.GetName());
			IsEnd = FightLogic(_Player, NewMonster, _Player, NewMonster);
		}
		else {
			printf_s("%s의 선공\n", NewMonster.GetName());
			IsEnd = FightLogic(NewMonster, _Player, _Player, NewMonster);
		}

		if (IsEnd == true)
		{
			return;
		}
	}
}

bool FightZone::FightLogic(FightUnit& _First, FightUnit& _Second, FightUnit& _Top, FightUnit& _Bot)
{
	{
		int Input = _getch();
	}
	system("cls");

	// First가 선공
	_Second.DamageLogic(_First);
	_Top.StatusRender();
	_Bot.StatusRender();
	_Second.DamageRender();
	if (_Second.IsDeath() == true)
	{
		printf_s("게임 종료\n");
		return true;
	}

	{
		int Input = _getch();
	}
	system("cls");

	// Second가 후공
	_First.DamageLogic(_Second);
	_Top.StatusRender();
	_Bot.StatusRender();
	_Second.DamageRender();
	_First.DamageRender();
	if (_First.IsDeath() == true)
	{
		printf_s("게임 종료\n");
		return true;
	}

	{
		int Input = _getch();
	}
	system("cls");

	return false;
};
profile
⋆꙳⊹⋰ 𓇼⋆ 𝑻𝑰𝑳 𝑨𝑹𝑪𝑯𝑰𝑽𝑬 ⸝·⸝⋆꙳⊹⋰

0개의 댓글