[TIL] 23-12-21

Lev·2023년 12월 21일
0

📓TIL Archive

목록 보기
9/33

class

Access Specifier : 접근제한지정자

  • 객체가 값형이거나 레퍼런스형일 때 .을 누르면 객체를 사용할 수 있다.
  • 하지만 객체의 모든 것을 사용할 수 있는 것은 아니다.
  • 클래스의 기본은 캡슐화이기 때문

💡 객체지향프로그래밍의 4대 특징
1. 추상화 ⇒ 현실에 존재하는 대상의 특성과 기능을 파악하여 정의한다.
2. 상속
3. 다형성
4. 캡슐화 ⇒ 객체가 모든 것을 외부에 공개할 필요는 없다.

// 외부(전역)

class Monster
{
	// 멤버

public:
	int publicint;
	void publicfunction()
	{
		protectedint = 20;
		protectedfunction();

		privateint = 20;
		privatefunction();
	}

protected:
	int protectedint;
	void protectedfunction()
	{

	}

private:
	int privateint;
	void privatefunction()
	{

	}
};

class Player
{
	// 외부

public:
	int GetHp()
	{
		return Hp;
	}
	void SetHp(int _Value)
	{
		if (_Value == 0)
		{

		}
		Hp = _Value;
	}
	// 멤버면수는 private에 두고, get 함수와 set 함수를 만들어 사용한다
	// 절대 변수를 외부에서 그냥 접근하지 못하도록 하는 것이 안전하고 좋다

protected:

private:
	int Hp;
	int Att;
	// 변수는 기본적으로 private 또는 protected
};

int TestValue = 200;
int GetTest()
{
	return TestValue;
}

class BossMonster
{
	// 디폴트 접근제한지정자는 private이다
	int Hp;
	int Att;
};

int main()
{
	// 외부(지역)

	int Value = GetTest();	// 200
	Value = 3000;	// 3000

	Monster NewMonster = Monster();
	// NewMonster. => 라고 쳤을 때 publicint와 publicfunction에만 접근이 가능하다
	Player NewPlayer = Player();

	NewPlayer.SetHp(100);	// Hp = 100
	NewPlayer.SetHp(0);	// Hp = 0
}

전역 + 지역은 합쳐서 외부라고 볼 수 있다.

  • private
    • 접근 가능 여부 ⇒ 외부(불가능), 자식(불가능), 멤버(가능)
    • 생략하면 디폴트로 설정된다
  • protected
    • 접근 가능 여부 ⇒ 외부(불가능), 자식(가능), 멤버(가능)
  • public
    • 접근 가능 여부 ⇒ 외부(가능), 자식(가능), 멤버(가능)
// int와 class는 크게 다르지 않다...
class Player
{

};

int main()
{
	int Value = int();
	int& RefValue = Value;

	Player NewPlayer = Player();
	Plyer& RefPlayer = NewPlayer;
}

struct

// Q1. C++에서 struct와 class의 차이는?
// Q2. C의 struct와 c++의 struct와 class의 차이는?

struct Monster
{
	// 디폴트 접근제한지정자는 public이다 (1)
	// 그 이외에 class와의 차이점은 없다
	// 앞으로 배우게 될 모든 class 문법은 struct에도 적용된다
	int Hp;
	int Att;
};

int main()
{
	Monster NewMonster;
	// NewMonster. => 라고 치면 Hp와 Att에 접근 가능
}

/*
// C에서의 struct는 개념이 아니고, 단순한 데이터의 집합이다 (2)
// 접근제한지정자도 없다 => 모든 요소는 public처럼 취급된다
// 내부에 함수도 작성할 수 없다

struct _Monster
{
	int Value;
};
typedef struct _Monster Monster;

typedef struct _Player
{
	int Value;
} Player;

int main()
{
	Monster NewMonster;
	NewMonster.Value;
}
*/

멤버

멤버에 접근하려면 무조건 객체가 필요하다!

  • 클래스는 설계도이고, 객체가 실체이기 때문
  • 객체가 생성될 때 멤버변수도 실체를 갖는다 (멤버함수는… 다음번에 추가설명)

.라는 멤버접근연산자를 사용해서 멤버변수와 멤버함수에 접근할 수 있다.

class Player
{
public:
	int Hp;	// 멤버변수

	void Damage()	// 멤버함수
	{

	}
};

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

	NewPlayer.Damage();
	NewPlayer.Hp;
}

Constructor : 생성자

객체를 만드는 함수

멤버함수... 이지만 멤버함수의 규칙에 따르지 않는다.

  • 객체가 없어도 호출이 가능하다
  • 리턴값이 없다 ⇒ 자신을 포함하고 있는 객체를 만들어 리턴한다.
  • 무조건 클래스의 이름과 동일한 이름을 가진다
  • 초기화를 위한 멤버이니셜라이저 문법을 유일하게 사용할 수 있다
  • 생산자를 아무것도 만들어두지 않으면 객체가 생성될 때 컴파일러가 디폴트 생성자를 만들어준다
  • 대신, 어떠한 형태로든 생성자를 만들었다면 디폴트 생성자를 만들어주지 않는다
// 전역
int GlobalVar = 10;	// 전역변수

class Monster
{
	// 멤버
private:
	int Hp = 20;	// 멤버변수
	// 리터럴 초기화 (1)
};

class Player
{
public:
	int Hp;
};

class NPC
{
public:
	// 생성자
	NPC() : Hp(10), Att(10)	// 초기화 => 멤버 이니셜라이저 문법 (3)
	{
		Hp = 20;	// 대입
	}

private:
	int Hp;
	int Att;
};

class Test
{
public:
	int Value = 10;

	Test() : 
		Value(20)	// 멤버 이니셜라이저 문법 (3)
					// 이렇게 엔터를 쳐서 아래로 내려도 인식된다
	{

	}
};

int main()
{
	// 지역
	int LovalVar = 10;	// 지역변수

	Monster NewMonster = Monster();	// 지역변수
	Player NewPlayer = { 10 };	// 이니셜라이즈 리스트 초기화 (2)

	NPC NewNpc = NPC();
	int LocalVar = int();

	int Value = 0;	// 초기화 : 메모리가 만들어짐과 동시에 값을 넣어준다
	Value = 0;	// 대입 : 메모리가 만들어진 후에 값을 넣어준다
}

초기화는 되도록 되어있는 것이 좋고, 대입과는 차이가 있다.

class의 멤버변수를 초기화하는데에는 세가지 방법이 있다.

  • 리터럴 초기화 (1)
    • 가장 최신 방법
  • 이니셜라이즈 리스트 초기화 (2)
    • public인 변수들은 배열처럼 초기화 가능
    • 단 모든 변수가 public일 때만 가능하다.
    • 리터럴 초기화와 동시에 사용될 경우, 이니셜라이즈 리스트 초기화가 더 나중에 적용된다.
  • 멤버 이니셜라이저 문법 (3)

overloading : 오버로딩

이름이 같은데 인자가 다른 여러개의 함수를 만드는 것

  • 사실 이름이 같은게 아니다… 실제로는 이름에 인자를 포함하고 있기 때문
  • 리턴값은 이름에 포함되지 않는다.
  • 모든 규격의 함수(전역함수, 지역함수, 멤버함수, 생성자 등)에 사용된다.
void Test()	// Test(void)
{

}

void Test(int _Value)	// Test(int)
{

}

void Test(char _Value)	// Test(char)
{

}

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(10)
	Monster NewMonster3 = { 10 };	// Monster(10)
	Monster NewMonster4 = { 10, 10, 10 };	// Monster(10, 10, 10)
}

operator overloading : 연산자 오버로딩

클래스의 함수를 만들 때, 연산자 형식으로 사용할 수 있게 만드는 것

  • 보통 수학 관련 사용자 정의 자료형을 만들 때 많이 사용된다. ⇒ 직관적으로 이해할 수 있기 때문
  • 그 외에 복사의 목적으로 =이 사용되곤 한다. ⇒ 중요함!
class MyInt
{
private:
	int Value = 0;

public:
	MyInt()
		// : Value(0) 로 초기화해도 된다
	{

	}

	MyInt(int _Value)
		: Value(_Value)
	{

	}

	MyInt operator-(MyInt _Other)
	{
		return Value - _Other.Value;
	}
};

class Player
{
public:
	void operator=(const Player& _Other)	// 복사
	{
		Hp = _Other.Hp;
		Att = _Other.Att;
		Def = _Other.Def;
		Cri = _Other.Cri;
		Exp = _Other.Exp;
		Gold = _Other.Gold;
	}
	// 레퍼런스이므로 8byte만 사용해 빠르다
	// 레퍼런스이므로 함수 바깥의 값에 실제적으로 영향을 준다
	// const이기 때문에 인자의 값이 바뀔 가능성이 없다
	// 레퍼런스이기 때문에 nullptr이나 비어있는 값이 인자로 들어오지 않는다 (포인터와의 차이)

	int Hp;
	int Att;
	int Def;
	int Cri;
	int Exp;
	int Gold;
	// 보통 이런 스테이터스는 private이긴 하다...
	// 직접 수정 가능해지면 곤란한 일이 생길 수 있기 때문
};

int main()
{
	{
		int Left = 20;
		int Right = 20;
		int Result = 0;

		Result = Left - Right;
	}
	{
		MyInt Left = 20;
		MyInt Right = 20;
		MyInt Result = 0;

		Result = Left.operator-(Right);	// Left - Right;
	}
	{
		Player NewPlayer0 = { 1, 1, 1, 1, 1, 1 };
		Player NewPlayer1 = { 2, 2, 2, 2, 2, 2 };

		NewPlayer0.operator=(NewPlayer1);	// NewPlayer0 = NewPlayer1
	}
}

⇒ 포인터나 레퍼런스를 사용하지 않으면
최소 클래스의 크기만큼 사용할 뿐더러, 함수 바깥의 값이 변하지 않는다

⇒ 하지만 포인터나 레퍼런스를 사용하면
8byte만 사용하면 되고, 함수 바깥의 값에 영향을 준다

따라서 함수 호출 시 객체를 인자로 받을 때에는, 값형이 아니라 포인터 또는 레퍼런스로 받아야 한다!!!

Text RPG (class ver.)

#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++)
		{
			int a = 0;
			printf_s("-");
		}

		printf_s("\n");	// (1)
		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;
	}

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++)
		{
			int a = 0;
			printf_s("-");
		}

		printf_s("\n");	// (1)
		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;
	}

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();
		}

		if (NewPlayer.IsDeath() || NewMonster.IsDeath())
		{
			printf_s("게임 종료!\n");
			break;
		}

		system("cls");
		NewPlayer.DamageLogic(NewMonster.GetAtt());

		NewPlayer.StatusRender();
		NewMonster.StatusRender();
		NewPlayer.DamageRender();
		NewMonster.DamageRender();

		{
			int Input = _getch();
		}

		if (NewPlayer.IsDeath() || NewMonster.IsDeath())
		{
			printf_s("게임 종료!\n");
			break;
		}

		system("cls");
	}
}

📢 코딩스탠다드
가급적 유지하자!

  1. 전역변수와 지역변수는 이름을 구분하여 작성하자
  2. 변수에는 반드시 초기값을 설정하자
  3. 함수의 인자 앞에는 언더바를 붙이자
  4. 포인터를 초기화할 때 절대 0을 사용하지 말고, nullptr을 사용하자
  5. 경로, 함수명, 변수명에 한글은 절대 쓰지 말자
  6. 프로젝트 생성 시 바탕화면은 절대 피하자
  7. if문을 사용할 때, 한 줄 코드일지라도 반드시 중괄호를 사용하자
  8. 함수의 리턴값을 꼭 변수로 받아서 확인해보자
  9. 클래스를 정의하면 일단 public, protected, private 꼭 작성해두자
    그리고 순서를 바꾸거나, 이후에 밑에서 한번 더 접근제한지정자를 사용하거나 하지는 말자 (new!)

📢 기억해두면 좋은 VS단축키 & 정보

  • Shift + Up or Down ⇒ 한 줄 드래그
  • Alt + 드래그 ⇒ 지정 사각형만큼 드래그 (+ 방향키를 누르면 커서가 다같이 움직인다)
  • Ctrl + A ⇒ 문서 코드 전체 선택
  • Shift + Del ⇒ 한 줄 삭제
  • 드래그 + Alt + Up or Down ⇒ 윗줄 또는 아랫줄로 이동
  • 드래그 + Ctrl + K+ C ⇒ 주석
  • Ctrl + K + F ⇒ 자동 줄맞춤
  • 파일명 위에 커서 + Ctrl + Shift + G ⇒ 파일 열기
  • 이름 위에 커서 + F12 ⇒ 정의로 이동
profile
⋆꙳⊹⋰ 𓇼⋆ 𝑻𝑰𝑳 𝑨𝑹𝑪𝑯𝑰𝑽𝑬 ⸝·⸝⋆꙳⊹⋰

0개의 댓글