[C#] Object Oriented Programming#01 | 객체 지향, 복사와 참조, 스택과 힙
by Sugar0810※ 객체 지향의 특징 및 절차 지향과 차이점
- 절차 지향 - 코드가 전부 함수로 구성.
- 프로그램 규모가 커지면 복잡해진다.(유지 보수 면에서 불리)
- 함수 호출 순서에 종속적이기 때문에 호출 순서 타고타고 올라가 보며 코드를 읽어보아야 해서 불편하다.
- 함수를 계속 새로 만들고 만들고 해야해서 복잡하다.
- 같은 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만으로는 메모리 할당이 되어 있지 않아 객체 생성이 안됨
- C++과 달리 C# 에선 객체 생성시
※ 복사와 참조
구조체와 클래스의 차이
구조체(Struct) -> Call by Value 방식으로 작업한다. (복사)
- 예를 들어 이름이
mage
인Mage
타입의 구조체(Struct)가 있다면Mage mage2 = mage
하여도mage
와mage2
는 별개의 메모리다.mage2
는mage
의 값들을 깊은 복사해 온 사본일 뿐이다. - 객체와 달리 구조체는
Mage mage;
만 해도 메모리가 할당이 된다.
클래스(Class) -> Call by Reference 방식으로 작업한다. (참조)
- 예를 들어 이름이
knight
인Knight
클래스 타입의 객체가 있다면Knight knight2 = knight
하면knight2
와knight
는 동일한 메모리를 참조하게 된다. 즉 이 둘은 동일한 객체를 접근하다. 데이터를 복사해준 것이 아닌 얕은 복사하여 동일한 객체에 대한 주소를 내부적으로 복사해준 것 뿐이다.- C++로 따지면 두 변수가 동일한 메모리에 소유권을 모두 가지도록
shared_ptr
를 만들고 리턴해준 상황과 같다.
- C++로 따지면 두 변수가 동일한 메모리에 소유권을 모두 가지도록
- C#에선
Knight knight;
만으로는 메모리 할당이 되지 않는다.new
를 사용하여야 메모리가 할당이 된다.(=실존하게 된다.)new
를 사용하여 할당했다는 것은 힙 메모리에 동적으로 할당 받았다는 것이다. C# 에서 클래스 객체는 C++과 달리 무조건 힙 메모리에 올라간다.- C++은 구조체나 클래스 객체나 다 스택, 힙 양쪽 다 사용이 가능하다.
- 힙과 다르게 스택 메모리는 유효 범위가 계속 달라지므로 스택의 주소를 참조하는건 좀 위험한 일이다.
객체 복사시 참조가 아닌 깊은 복사를 하고 싶다면
새로운 별개의 객체를 생성하여 자기 자신의 속성 값들을 복사하고 이를 리턴하는 함수 Clone()
을 만든다. 직접 Call by Value (복사)를 구현하는 방식.
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 knight2 = knight.Clone();
※ 스택과 힙
스택(Stack) 메모리
- 불완전하고 일시적으로 사용하는 메모리(=메모장)
- 함수 내부에서만 살아 있다가 함수 끝나면 사라지는 지역 변수, 연쇄적인 함수들의 호출 위치 등등 잠깐 있다 사라질 것들은 스택 메모리에 올라온다.
- 스택 자료구조처럼 쌓이는 구조고 가장 나중에 쌓인게 가장 먼저 빠진다.(=FIFO, 선입선출)
- 반면에 객체와 같은 Call by Reference 는 스택 메모리에 그 데이터 자체가 아닌 그 데이터가 있는 주소가 올라간다.
- 실제 데이터는 힙 메모리에 있다. 즉 스택 메모리에 저장되는건 본체 데이터가 위치한 힙 메모리의 주소다.
힙(Heap) 메모리
new
등등 프로그램 실행 中 실시간으로 할당 된 것들이 올라간다.- 실행 전부터 메모리를 잡고 실행하는 것이 아니라 프로그램 실행 중에 그때 그때 필요한 메모리를 할당할 땐 힙 메모리에서 가져다 쓴다.
- 특별히 해제해주는 작업이 없다면 프로그램 내내 힙 메모리에 안정적으로 남아있게 된다.
- C++ 에선 개발자가 반드시 직접
delete
로 일일이 해제 해주어 힙 메모리 누수를 막아야 했었다. - C# 에선 아무도 참조하지 않고 자리만 차지하는 힙 메모리는 C# 시스템 자체의 GC(가비지 콜렉터)가 알아서 해제해준다.
- C++ 에선 개발자가 반드시 직접
- Call by Reference 를 실현하는
ref
변수가 참조하는 데이터는 스택 메모리일 수도 있다.ref int a
에서 참조하는 메모리는 스택 메모리 일 수도 있는 것.a
엔 참조 중인 스택 메모리의 주소가 스택 메모리에 속한 a에 올라가게 된다.- Call by Reference 라고 해서 무조건 힙에 저장되는건 아니라는 것이다. 착각하지 말기!
※ 참고 사이트
'💻 Computer Science > Object Oriented Programming' 카테고리의 다른 글
블로그의 정보
Sugar
Sugar0810