반응형
객체 지향
[절차 지향 👉 전부 함수로 코드를 짠다.]
- 유지 보수가 힘들다. 프로그램 규모가 커지면 복잡해진다.
- 함수 호출 순서에 종속적이기 때문에 유지 보수가 힘들다. 호출 순서 타고타고 올라가 보며 코드를 읽어보아야 해서 불편하다.
- 함수를 계속 새로 만들고 만들고 해야해서 복잡하다.
- 같은 Fight 이라도 플레이어 vs 플레이어, 보스 vs 플레이어 등등 많은 버전이 있어야 하므로 오버로딩을 많이 해야 해서 복잡해진다.
[객체 지향 👉 모든 것을 객체 위주로 생각한다.]
- 속성(멤버 변수)와 기능(멤버 함수)로 나뉜다.
- ‘플레이어’의 이름, 공격력, HP, 직업 같은 속성들과 Fight 같은 기능을 추상화 하여 Player라는 이름의 클래스로 묶는다. 이 클래스(붕어빵틀, 설계도)를 가지고 세상에 존재하는 객체(실제 붕어빵)로 찍어낼 수 있다.
class Knight
{
public int hp;
public int attack;
public void Move() { Console.Write("Kight Move"); }
public void Attack() { Console.Write("Kight Attack"); }
}
class Program
{
static void Main(string [] args)
{
Knight knight = new Knight();
knight.hp = 100;
knight.attack = 10;
knight.Move();
knight.Attack();
}
}
[Knight 클래스 (설계도)]
1. 속성
- hp, attack 👉 실존하는 모든 Knight 타입 객체들은 이 속성을 가진다.
2. 기능
- Move(), Attack() 👉 실존하는 모든 Knight 타입 객체들은 이 기능을 가진다.
클래스는 단순 설계도일 뿐 이 클래스로 찍어낸 Knight 객체들은 속성 값이 제각각 다를 수 있다. 또한 객체 생성시 new를 사용하여 생성해야 한다. Kignt knight만으로는 객체 생성이 되지 않으며, 메모리 할당이 되어 있지 않는다.
복사와 참조
[구조체와 클래스의 차이]
구조체 👉 Call by Value 방식으로 작업한다. (복사)
- 예를 들어 이름이 mage인 Mage타입의 구조체(Struct)가 있다면 Mage mage2 = mage 하여도 mage와 mage2는 별개의 메모리다. mage2는 mage의 값들을 깊은 복사해 온 사본일 뿐이다.
- 객체와 달리 구조체는 Mage mage; 만 해도 메모리가 할당이 된다.
클래스 👉 Call by Reference 방식으로 작업한다. (참조)
- 예를 들어 이름이 knight인 Knight 클래스 타입의 객체가 있다면 Knight knight2 = knight 하면 knight2와 knight는 동일한 메모리를 참조하게 된다. 즉 이 둘은 동일한 객체를 접근하다. 데이터를 복사해준 것이 아닌 얕은 복사하여 동일한 객체에 대한 주소를 내부적으로 복사해준 것 뿐이다.
- C#에선 Knight knight;만으로는 메모리 할당이 되지 않는다. new를 사용하여야 메모리가 할당이 된다.(=실존하게 된다.) new를 사용하여 할당했다는 것은 힙 메모리에 동적으로 할당 받았다는 것이다.
- C# 에서 클래스 객체는 C++과 달리 무조건 힙 메모리에 올라간다.
- 힙과 다르게 스택 메모리는 유효 범위가 계속 달라지므로 스택의 주소를 참조하는건 좀 위험한 일이다.
[객체 복사시 참조가 아닌 깊은 복사를 하고 싶다면]
class Knight
{
public int hp;
public int attack;
public Knight Clone()
{
Knight knight = new Knight();
knight.hp = hp;
knight.attack = attack;
return knight;
}
public void Move() { Console.Write("Kight Move"); }
public void Attack() { Console.Write("Kight Attack"); }
}
...
Knight knight2 = knight.Clone(); // knight을 깊은 복사하여 knight2을 만든다. 두 객체는 별개의 객체이다.
새로운 별개의 객체를 생성하여 자기 자신의 속성 값들을 복사하고 이를 리턴하는 함수 Clone()을 만든다. 직접 Call by Value를 구현하는 방식이다.
스택과 힙
[스택 메모리]
- 불완전하고 일시적으로 사용하는 메모리 메모장 같은 존재.
- 함수 내부에서만 살아 있다가 함수 끝나면 사라지는 지역 변수, 연쇄적인 함수들의 호출 위치 등등 잠깐 있다 사라질 것들은 스택 메모리에 올라온다. (스택 자료구조처럼 쌓이는 구조고 가장 나중에 쌓인게 가장 먼저 빠진다.)
- 반면에 객체와 같은 Call by Reference 는 스택 메모리에 그 데이터 자체가 아닌 그 데이터가 있는 주소가 올라간다. 실제 데이터는 힙 메모리에 있다. 즉 스택 메모리에 저장되는건 본체 데이터가 위치한 힙 메모리의 주소다.
[힙 메모리]
- new 등등 프로그램 실행 中 실시간으로 할당 된 것들이 올라간다. 실행 전부터 메모리를 잡고 실행하는 것이 아니라 프로그램 실행 중에 그때 그때 필요한 메모리를 할당할 땐 힙 메모리에서 가져다 쓴다.
- 특별히 해제해주는 작업이 없다면 프로그램 내내 힙 메모리에 안정적으로 남아있게 된다.
- C++ 에선 개발자가 반드시 직접 delete 로 일일이 해제 해주어 힙 메모리 누수를 막아야 했었다. C# 에선 아무도 참조하지 않고 자리만 차지하는 힙 메모리는 C# 시스템 자체의 가비지 콜렉터가 알아서 해제해준다.
- Call by Reference 를 실현하는 ref 변수가 참조하는 데이터는 스택 메모리일 수도 있다.
- ref int a 에서 참조하는 메모리는 스택 메모리 일 수도 있는 것. a엔 참조 중인 스택 메모리의 주소가 스택 메모리에 속한 a에 올라가게 된다.
- Call by Reference 라고 해서 무조건 힙에 저장되는건 아니라는 것이다. 착각하지 말기!
반응형
'공부 > 인프런 - Rookiss' 카테고리의 다른 글
Part 1-4-3. 객체지향 : 상속성, 은닉성 (0) | 2023.08.02 |
---|---|
Part 1-4-2. 객체지향 : 생성자, static (0) | 2023.08.02 |
Part 1-3-1. TextRPG : 디버깅 기초, 직업, 플레이어/몬스터 생성, 전투 (0) | 2023.08.02 |
★ Part 1-2-2. 코드의 흐름 제어 : 함수, ref, out (0) | 2023.08.02 |
Part 1-2-1. 코드의 흐름 제어 : if와 else, switch, 상수와 열거형, while, for, break, continue (0) | 2023.07.31 |