부모의 함수를 사용하고 싶다면, 풀네임을 사용하는 것이 정석 (이지만 사실 필수는 아니다.)
부모가 자식보다 먼저 호출된다 ⇒ 그래야 자식이 부모의 멤버변수나 멤버함수를 사용할 수 있다.
#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 생성자
}
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을 상속받지 않아 불가능
}
자식클래스의 객체는 언제든 부모클래스의 참조형이 될 수 있다!
굉장히 권장되는 암시적 형변환이다.
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);
}
부모클래스의 참조형에서 자식클래스의 참조형으로 형변환하는 것
건강하지 못한 캐스팅이라 권장되지 않는다...
구분방법을 마련해둘 수 있긴 하지만, 인간인지라 언제든 실수할 여지가 있기도 하다.
그래도 절대 사용하지 않는건 아니다.
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 변수가 이상한 값이 된다
// 어딘지 알 수 없는 메모리를 사용하게 된다
}
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;
};
// [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) 부모클래스의 생성자를 이용해 자식클래스를 초기화하는 방법
// [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;
};