class Monster {
};
int main()
{
// 선생님 기준 int도 class다.
// int가 할 수 있는데 대 클래스가 못한다? -> 내가 안 만든 것이다.
// int가 할 수 없는데 내 클래스는 한다. -> 그건 내가 만들어준거다.
// 내가 원한다면 int와 완전히 동일한 클래스도 만들 수 있다.
// 즉 그냥 내부에 뭐가 구현되어 있냐 안되어있냐 차이
Monster NewMonster = Monster();
return 0;
}
class Player {
public:
// 멤버 함수
void Damage() {
}
// 멤버 변수
int Hp;
// 일반적인 멤버들은 사용을 위해서는 무조건 객체가 필요하다.
};
int main() {
// 객체 만들기
Player NewPlayer = Player();
// 객체 멤버접근연산자 함수
NewPlayer . Damage();
NewPlayer.Hp = 20;
return 0;
}
클래스에는 접근 지정자(접근 제한자)라고 불리는 문법이 있다.
public, protected, private가 있다.
클래스는 디폴트 접근 지정자가 private이다. 명시하지 않으면 private으로 지정된다.
외부(전역+지역)에서는 public 지정자 아래 있는 변수나 함수들만 이용할 수 있다.
private이나 protected는 클래스의 같은 내부에서만 사용할 수 있다.
변수는 보통 private나 protected로 해두고 Get, Set 함수를 만들어서 접근한다. 클래스의 멤버변수를 변경할 수 있는 함수가 제한되기 때문에 디버깅에 용이하다.
다른 클래스의 내부는 외부이다.
ex) Monster 클래스에게 Player 클래스의 내부는 외부지역이다.
외부 | 자식 | 멤버 |
---|---|---|
O | O | O |
외부 | 자식 | 멤버 |
---|---|---|
X | O | O |
클래스 내부는 기본이 private이다.
외부 | 자식 | 멤버 |
---|---|---|
X | X | O |
#include <iostream>
// 전역
class Monster {
// 클래스의 내부 : 멤버
public:
int publicint;
void publicFunction() {
}
protected:
int protectedint;
void protectedFunction() {
}
private:
int privateint;
void privateFunction() {
}
};
int main()
{
// 지역(외부)
Monster NewMonster = Monster();
return 0;
}
클래스의 기본 특성은 캡슐화이다. 객체지향의 4대 특성으로 추상화, 상속, 다형성, 캡슐화가 있다.
그 중에 추상화는 간단하게만 말하면 추상적인 인간의 생각을 문법으로 표현한다는 특징이다. 캡슐화는 객체가 모든 것을 외부에 공개할 필요는 없다는 특징이다. (상속과 다형성은 나중에)
struct Monster {
int Hp;
int Att;
};
struct는 디폴트 접근 지정자가 public이다.
그 이외의 차이점이 없다. 내부 변수를 private으로 지정하면 class와의 차이점이 아예 없다.
앞으로 배우게될 모든 문법이 struct에도 그냥 적용된다.
c에서의 struct는 데이터의 집합이다. 함수도 가질 수 없다.
접근 지정자가 존재하지 않는다.
이름 생략이 불가능해서 struct Monster로 부른다. typedef로 이름을 변경해서 사용한다.
클래스의 멤버변수를 초기화하는 방법이 많다. 3가지 있다.
보기에 가장 직관적.
class Monster {
private:
int Hp = 20; // 클래스 안에 있으면 멤버변수라고 부른다.
};
public인 변수는 배열 초기화하는 것처럼 초기화할 수 있다.
모든 변수가 public일때만 가능하다.
class Player {
public:
int Hp;
};
int main() {
Player NewPlayer = { 10 };
return 0;
}
생성자는 멤버함수의 규칙에서 예외가 된다. 리턴값도 없다.
1. 리턴값이 없다. → 자기 자신을 포함하고 있는 그 객체를 만들어서 리턴하니까.
2. 생성자는 객체 없이도 호출이 가능하다.
3. 무조건 클래스의 이름과 동일해야 한다. → 사용 편의성을 위한 규칙
4. 초기화를 위한 멤버이니셜라이저 문법이라는 것을 유일하게 사용할 수 있다.
class NPC {
public:
NPC() : Hp(10){ // 멤버이니셜 라이저 문법 => 생성자() : 멤버이름()
}
private:
int Hp;
};
// 두 개일 경우
class NPC {
public:
NPC() : Hp(100), Att(10) { // 멤버이니셜 라이저 문법 => 생성자() : 멤버이름()
}
private:
int Hp;
int Att;
};
이름이 같은데 인자가 다른 함수를 여러 개 만들 수 있는 규칙을 오버로딩이라고 한다.
이 오버로딩 규칙은 모든 규격의 함수에 사용이 가능하다. 생성자에도 오버로딩이 가능하다.
인자가 다르면 같은 이름의 함수를 여러 개 만들 수 있다.
정확히는 이름이 같은 게 아니다. 이름에 인자가 포함된다고 생각하면 된다.
아래 두 함수는 각각
void Test(void)
void Test(int)
함수이다.
void Test() {
}
void Test(int _Value) {
}
class Monster {
public:
// 다 생성자
Monster() {
}
Monster(int _Value) {
}
Monster(int _Value0, int _Value1, int _Value2) {
}
};
int main() {
Monster NewMonster0 = Monster();
Monster NewMonster1 = Monster(10);
Monster NewMonster2 = 10;
Monster NewMonster3 = { 10, 10, 10 };
return 0;
}
보통 수학관련 자료형을 만들 때 많이 사용한다.
연산자 겹지정은 클래스의 함수를 만들 때 연산자 형식으로 사용할 수 있게 만드는 함수다.
const Player&
로 레퍼런스이기 때문에 전체 객체를 복사하기보다 8바이트만 사용해서 값을 가져올 수 있다. nullptr이나 비어있는 값이 인자로 들어오지 않는다.class Player {
public:
void operator=(const Player& _Other) {
Hp = _Other.Hp;
Att = _Other.Att;
Def = _Other.Def;
Cri = _Other.Cri;
Exp = _Other.Exp;
}
int Hp;
int Att;
int Def;
int Cri;
int Exp;
// ....
};
int main() {
Player NewPlayer0 = { 1, 1, 1, 1, 1 };
Player NewPlayer1 = { 2, 2, 2, 2, 2 };
NewPlayer0 = NewPlayer1;
// NewPlayer0.operator=(NewPlayer1); 와 같다.
return 0;
}
class MyInt {
public:
MyInt() {
}
MyInt(int _Value) : Value(_Value) {
}
MyInt operator-(MyInt _Other) {
return Value - _Other.Value;
}
MyInt operator+(MyInt _Other) {
return Value + _Other.Value;
}
private:
int Value = 0;
};
int main() {
MyInt Left = 20;
MyInt Right = 20;
MyInt Result = 0;
Result = Left - Right; // => Result = Left.operator-(Right); 와 같음
Result = Left + Right; // => Result = Left.operator+(Right); 와 같음
return 0;
}
Shift + delete
: 한 줄 삭제
저번 시간에 만든 TextRpg에 클래스를 적용시킨 코드이다. Player와 Monster클래스의 내용이 거의 반복된다. 나중에 상속을 배우고 나서 개선된다.
#include <iostream>
#include <conio.h>
class Player
{
public:
Player()
{
}
void StatusRender()
{
int Size = printf_s("%s ", Name);
for (int i = 0; i < 50 - Size; i++)
{
printf_s("-");
}
printf_s("\n");
printf_s("공격력 %d\n", Att);
printf_s("체력 %d\n", Hp);
for (int i = 0; i < 50; i++)
{
printf_s("-");
}
printf_s("\n");
}
void DamageLogic(int _Att)
{
Hp -= _Att;
}
int GetAtt()
{
return Att;
}
void DamageRender()
{
printf_s("%s 가 %d의 공격력으로 공격했습니다.\n", Name, Att);
}
protected:
private:
char Name[100] = "Fighter";
int Hp = 100;
int Att = 10;
};
class Monster
{
public:
Monster()
{
}
void StatusRender()
{
int Size = printf_s("%s ", Name);
for (int i = 0; i < 50 - Size; i++)
{
printf_s("-");
}
printf_s("\n");
printf_s("공격력 %d\n", Att);
printf_s("체력 %d\n", Hp);
for (int i = 0; i < 50; i++)
{
printf_s("-");
}
printf_s("\n");
}
void DamageLogic(int _Att)
{
Hp -= _Att;
}
int GetAtt()
{
return Att;
}
void DamageRender()
{
printf_s("%s 가 %d의 공격력으로 공격했습니다.\n", Name, Att);
}
protected:
private:
char Name[100] = "Orc";
int Hp = 50;
int Att = 5;
};
int main()
{
Player NewPlayer = Player();
Monster NewMonster = Monster();
while (true)
{
NewPlayer.StatusRender();
NewMonster.StatusRender();
{
int Input = _getch();
}
system("cls");
NewMonster.DamageLogic(NewPlayer.GetAtt());
NewPlayer.StatusRender();
NewMonster.StatusRender();
NewPlayer.DamageRender();
{
int Input = _getch();
}
system("cls");
NewPlayer.DamageLogic(NewMonster.GetAtt());
NewPlayer.StatusRender();
NewMonster.StatusRender();
NewPlayer.DamageRender();
NewMonster.DamageRender();
// 아무키나 눌릴때까지 기다려 줍니다.
{
int Input = _getch();
}
// 콘솔창에 명령을 요청한다.
system("cls");
}
}
Player와 Monster 중에 누군가 죽었다면 게임이 끝나도록 만들기.
Player와 Monster에 bool을 리턴하는 IsDeath()라는 함수를 만들어서 Hp가 0 이하가 되면 true를 리턴해서 종료되게 하기.
#include <iostream>
#include <conio.h>
class Player
{
public:
Player()
{
}
void StatusRender()
{
int Size = printf_s("%s ", Name);
for (int i = 0; i < 50 - Size; i++)
{
printf_s("-");
}
printf_s("\n");
printf_s("공격력 %d\n", Att);
printf_s("체력 %d\n", Hp);
for (int i = 0; i < 50; i++)
{
printf_s("-");
}
printf_s("\n");
}
void DamageLogic(int _Att)
{
Hp -= _Att;
}
int GetAtt()
{
return Att;
}
void DamageRender()
{
printf_s("%s 가 %d의 공격력으로 공격했습니다.\n", Name, Att);
}
bool IsDeath() {
/*if (Hp <= 0) {
return true;
}
else {
return false;
}*/
// 같은 의미
return Hp <= 0;
}
private:
char Name[100] = "Fighter";
int Hp = 100;
int Att = 10;
};
class Monster
{
public:
Monster()
{
}
void StatusRender()
{
int Size = printf_s("%s ", Name);
for (int i = 0; i < 50 - Size; i++)
{
printf_s("-");
}
printf_s("\n");
printf_s("공격력 %d\n", Att);
printf_s("체력 %d\n", Hp);
for (int i = 0; i < 50; i++)
{
printf_s("-");
}
printf_s("\n");
}
void DamageLogic(int _Att)
{
Hp -= _Att;
}
int GetAtt()
{
return Att;
}
void DamageRender()
{
printf_s("%s 가 %d의 공격력으로 공격했습니다.\n", Name, Att);
}
bool IsDeath() {
return Hp <= 0;
}
private:
char Name[100] = "Orc";
int Hp = 50;
int Att = 5;
};
int main()
{
Player NewPlayer = Player();
Monster NewMonster = Monster();
while (true)
{
NewPlayer.StatusRender();
NewMonster.StatusRender();
{
int Input = _getch();
}
system("cls");
NewMonster.DamageLogic(NewPlayer.GetAtt());
NewPlayer.StatusRender();
NewMonster.StatusRender();
NewPlayer.DamageRender();
if (NewMonster.IsDeath())
{
printf_s("게임 종료\n");
break;
}
{
int Input = _getch();
}
system("cls");
NewPlayer.DamageLogic(NewMonster.GetAtt());
NewPlayer.StatusRender();
NewMonster.StatusRender();
NewPlayer.DamageRender();
NewMonster.DamageRender();
{
int Input = _getch();
}
// 누군가 죽었다면 게임이 끝나야합니다.
// Player와 몬스터에 bool을 리턴하는 IsDeath()를 만들어서
// 누구든 Hp가 0이하가 되면 true를 리턴해서
// 종료가 되게 하세요.
if (NewPlayer.IsDeath())
{
printf_s("게임 종료\n");
break;
}
// 콘솔창에 명령을 요청한다.
system("cls");
}
}