Chapter 5-1. 객체 지향 : 객체 지향, 복사와 참조, 스택과 힙

Date:     Updated:

카테고리:

태그:

인프런에 있는 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 메모리 할당이 되어 있지 않음.


👱‍♀️ 복사와 참조

구조체와 클래스의 차이

  • 구조체 👉 Call by Value 방식으로 작업한다. (복사)
    • 예를 들어 이름이 mageMage타입의 구조체(Struct)가 있다면 Mage mage2 = mage 하여도 magemage2는 별개의 메모리다. mage2mage의 값들을 깊은 복사해 온 사본일 뿐이다.
    • 객체와 달리 구조체는 Mage mage; 만 해도 메모리가 할당이 된다.
  • 클래스 👉 Call by Reference 방식으로 작업한다. (참조)
    • 예를 들어 이름이 knightKnight 클래스 타입의 객체가 있다면 Knight knight2 = knight 하면 knight2knight는 동일한 메모리를 참조하게 된다. 즉 이 둘은 동일한 객체를 접근하다. 데이터를 복사해준 것이 아닌 얕은 복사하여 동일한 객체에 대한 주소를 내부적으로 복사해준 것 뿐이다.
      • C++로 따지면 두 변수가 동일한 메모리에 소유권을 모두 가지도록 shared_ptr를 만들고 리턴해준 상황과 같다.
    • 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# 시스템 자체의 가비지 콜렉터가 알아서 해제해준다.
    • Call by Reference 를 실현하는 ref 변수가 참조하는 데이터는 스택 메모리일 수도 있다.
      • ref int a 에서 참조하는 메모리는 스택 메모리 일 수도 있는 것. a엔 참조 중인 스택 메모리의 주소가 스택 메모리에 속한 a에 올라가게 된다.
      • Call by Reference 라고 해서 무조건 힙에 저장되는건 아니라는 것이다. 착각하지 말기!


🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

C Sharp 카테고리 내 다른 글 보러가기

댓글 남기기