Chapter 5-1. 객체 지향 : 객체 지향, 복사와 참조, 스택과 힙
Categories: C Sharp
Tags: C Sharp Programming
인프런에 있는 Rookiss님의 강의 Part1: C# 기초 프로그래밍 입문 를 듣고 정리한 필기입니다. 😀
👱♀️ 객체 지향
- 절차 지향 👉 전부 함수로 코드를 짬.
- 유지 보수가 힘들다. 프로그램 규모가 커지면 복잡해진다.
- 함수 호출 순서에 종속적이기 때문에 유지 보수가 힘들다. 호출 순서 타고타고 올라가 보며 코드를 읽어보아야 해서 불편하다.
- 함수를 계속 새로 만들고 만들고 해야해서 복잡하다.
- 같은 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 클래스 (설계도)
- 속성
hp
,attack
👉 실존하는 모든 Knight 타입 객체들은 이 속성을 가진다.
- 기능
Move()
,Attack()
👉 실존하는 모든 Knight 타입 객체들은 이 기능을 가진다.
- 속성
- 클래스는 단순 설계도일 뿐 이 클래스로 찍어낸 Knight 객체들은 속성 값이 제각각 다를 수 있다.
- 객체 생성 👉
new
사용- C++과 달리 C# 에선 객체 생성시
new
를 사용하여 생성해야 한다. Kignt knight
만으로는 객체 생성 X 메모리 할당이 되어 있지 않음.
- C++과 달리 C# 에선 객체 생성시
👱♀️ 복사와 참조
구조체와 클래스의 차이
- 구조체 👉 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++로 따지면 두 변수가 동일한 메모리에 소유권을 모두 가지도록
shared_ptr
를 만들고 리턴해준 상황과 같다.
- C++로 따지면 두 변수가 동일한 메모리에 소유권을 모두 가지도록
- C#에선
Knight knight;
만으로는 메모리 할당이 되지 않는다.new
를 사용하여야 메모리가 할당이 된다.(=실존하게 된다.)new
를 사용하여 할당했다는 것은 힙 메모리에 동적으로 할당 받았다는 것이다. C# 에서 클래스 객체는 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# 시스템 자체의 가비지 콜렉터가 알아서 해제해준다.
- C++ 에선 개발자가 반드시 직접
- Call by Reference 를 실현하는
ref
변수가 참조하는 데이터는 스택 메모리일 수도 있다.ref int a
에서 참조하는 메모리는 스택 메모리 일 수도 있는 것.a
엔 참조 중인 스택 메모리의 주소가 스택 메모리에 속한a
에 올라가게 된다.- Call by Reference 라고 해서 무조건 힙에 저장되는건 아니라는 것이다. 착각하지 말기!
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
Leave a comment