[문법을 뚫다]Call by reference, Call by value

2011. 8. 16. 12:48개발관련기록/Java

반응형

이번 장은 중요한 장입니다. 꼭 이해해야하는...

 

.Net의 형식은 클래스(class), 구조체(struct), 인터페이스(interface), 델리게이트(delegate), 열거형(enum) 이렇게 다섯 가지입니다.

또 .Net은 아래 두 가지 기반의 형식으로 나뉩니다. (상속 구조 그림 = > http://msdn.microsoft.com/ko-kr/library/ms173104.aspx)

  • 첫째, 참조 기반의 형식(class, interface, delegate)
  • 둘째, 값 기반의 형식 (struct, enum)

참조 기반 형식, 값 기반 형식 => http://msdn.microsoft.com/ko-kr/library/3ewxz6et.aspx

 

첫째, 참조 기반 형식의 특징은

  • 관리 힙에 할당됩니다.
  • System.Value 형식이 아닌 모든 형식으로부터 파생될 수 있습니다.
  • Sealed 형식이 아니라면 기본 형식으로 사용될 수 있습니다.

 

둘째, 값 기반 형식의 특징은

  • 스택 메모리에 할당됩니다.
  • 항상 System.Value 형식으로부터 파생됩니다.
  • Sealed 형식입니다. 기본 형식으로 사용될 수 없습니다.
  • 기본 생성자는 예약되어 있습니다.

두 기반 형식의 가장 큰 차이점은 참조 기반 형식은 관리 힙에 할당되고 값 기반 형식은 스택 메모리에 할당된다는 것입니다. 아시는 것처럼 힙은 객체를 할당하여 사용하지 않을 때 제거될 수 있으며 스택은 블록({}) 범위에서 메모리가 할당과 해제(제거) 됩니다.

 

 1, 대표적인 참조 기반 형식(class)과 대표적인 값 기반 형식(struct)

 C++에서 클래스(class)와 구조체(struct)는 완전히 동일한 것입니다.(디폴트 접근 한정자(클래스는 private, 구조체는 public) 만 빼면)

하지만 .Net의 클래스와 구조체는 전혀 다르게 동작합니다.

 클래스는 대표적인 참조 기반의 형식이며 구조체는 대표적인 값 기반의 형식입니다.

 

다음은 클래스와 구조체의 정의 및 사용 예제입니다.

  1. using System;

    class CPoint // Object에서 파생

    {

      private int x, y;

      public CPoint(int _x, int _y)

      {

        x = _x;

        y = _y;

      }

      public CPoint() // 클래스는 인자 없는 기본 생성자를 만들 수 있음.

      {

        x = 0;

        y = 0;

      }

      public void Print()

      {

        Console.Write("base:{0},  ",GetType().BaseType);

        Console.WriteLine("class:({0},{1})",x,y);

      }

    }

     

    struct SPoint // ValueType에서 파생

    {

      private int x, y;

      public SPoint(int _x, int _y)

      {

        x = _x;

        y = _y;

      }

      //public SPoint(){} // 구조체는 기본 생성자를 만들 수 없음.(예약됨)

      public void Print()

      {

        Console.Write("base:{0},  ", GetType().BaseType);

        Console.WriteLine("struct:({0},{1})", x, y);

      }

    }

    class Program

    {

      static void Main()

      {

        CPoint cpt = new CPoint(1, 1);

        cpt.Print();

     

        SPoint spt = new SPoint(5, 5);

        spt.Print();

      }

    }

base:System.Object,  class:(1,1)
base:System.ValueType,  struct:(5,5)

결과처럼 구조체(값 기반 형식)는 항상 ValueType으로부터 파생됩니다. 또 구조체(값 기반 형식)는 기본 생성자를 정의할 수 없습니다. 내부적으로 예약되어 있습니다.

 GetType()는 자신의 형식 객체를 반환합니다. BaseType은 이 객체의 속성으로 부모 형식을 반환합니다. 다음에 설명하므로 지금은 클래스와 구조체에 집중!

 

 다음은 위 예제의 메모리 그림입니다.

값_형식,_참조_형식_객체_메모리.PNG 

cpt는 힙 객체의 참조를 갖는 참조자(참조 변수)이며 spt는 객체 값 자체를 갖는 값 변수입니다. 또 cpt(참조자)는 Main() 함수 블록({})이 종료될 때 변수(참조자)는 바로 사라지지만 cpt가 가리키는 객체는 가비지 컬렉션 대상이 되는 힙 객체입니다. spt는 Main() 함수 블록({})이 종료될 때 변수와 객체(변수가 값 자체)가 모두 바로 사라지는 스택 객체입니다.

 

구조체는 기본 생성자 정의 없이도(내부적으로 예약됨) 호출할 수 있음!

  1. using System;

    class CPoint

    {

      private int x, y;

      public CPoint(int _x, int _y)

      {

        x = _x;

        y = _y;

      }

      public CPoint()

      {

        x = 0;

        y = 0;

      }

      public void Print()

      {

        Console.Write("base:{0},  ", GetType().BaseType);

        Console.WriteLine("class:({0},{1})", x, y);

      }

    }

     

    struct SPoint

    {

      private int x, y;

      public SPoint(int _x, int _y)

      {

        x = _x;

        y = _y;

      }

      //public SPoint(){}

      public void Print()

      {

        Console.Write("base:{0},  ", GetType().BaseType);

        Console.WriteLine("struct:({0},{1})", x, y);

      }

    }

    class Program

    {

      static void Main()

      {

        CPoint cpt = new CPoint();

        cpt.Print();

     

        SPoint spt = new SPoint();

        spt.Print();

      }

    }

base:System.Object,  class:(0,0)
base:System.ValueType,  struct:(0,0)

굿!

다음은 메모리 그림입니다.

값_형식,_참조_형식_객체_메모리2.PNG 

 

 

 구조체는 변수 선언이 곧 객체 생성입니다.

  1. using System;

    class CPoint

    {

      public int x, y;

      public void Print()

      {

        Console.Write("base:{0},  ", GetType().BaseType);

        Console.WriteLine("class:({0},{1})", x, y);

      }

    }

     

    struct SPoint

    {

      public int x, y;

      public void Print()

      {

        Console.Write("base:{0},  ", GetType().BaseType);

        Console.WriteLine("struct:({0},{1})", x, y);

      }

    }

    class Program

    {

      static void Main()

      {

        CPoint cpt// 객체가 없으므로 말도 않됨!

        cpt.x = 1; // 객체가 없는데 !

        cpt.y = 1; // 객체가 없는데 !

        cpt.Print();// 객체가 없는데 !

     

        SPoint spt// 변수 선언이  객체 생성이므로 당근 가능!!

     spt.x = 5;  // 굿!

        spt.y = 5;  // 굿!

        spt.Print();// 굿!

      }

    }

 클래스는 참조자(참조 변수)만 생성하고 객체를 생성하지 않아 오류!!

 구조체는 변수 선언이 곧 객체 생성이며 모든 멤버 변수 초기화 후 사용할 수 있습니다.

다음은 메모리 그림입니다.

값_형식,_참조_형식_객체_메모리3.PNG 

 

2, 참조 기반 형식(class)과 값 기반 형식(struct)의 복사

 다음 클래스와 구조체 객체의 복사 동작을 설명합니다.

 

클래스와 구조체의 복사 예제입니다.

  1. using System;

    class CPoint

    {

      public int x, y;

      public CPoint(int _x, int _y)

      {

        x = _x;

        y = _y;

      }

      public CPoint()

      {

        x = 0;

        y = 0;

      }

      public void Print()

      {

        Console.WriteLine("class:({0},{1})", x, y);

      }

    }

     

    struct SPoint

    {

      public int x, y;

      public SPoint(int _x, int _y)

      {

        x = _x;

        y = _y;

      }

      public void Print()

      {

        Console.WriteLine("struct:({0},{1})", x, y);

      }

    }

    class Program

    {

      static void Main()

      {

        CPoint cpt1 = new CPoint(1, 1);

        CPoint cpt2 = cpt1;

        cpt1.Print();

        cpt2.Print();

        Console.WriteLine();

     

        SPoint spt1 = new SPoint(5, 5);

        SPoint spt2 = spt1;

        spt1.Print();

        spt2.Print();

      }

    }

 class:(1,1)
class:(1,1)

struct:(5,5)
struct:(5,5)

클래스(참조 기반 형식)는 참조자가 복사되며 구조체(값 기반 형식)는 객체 자체가 복사됩니다.

 다음은 예제의 메모리 그림입니다.

참조와_값_복사.PNG 

 

 클래스는 두 참조자(cpt1,cpt2)가 하나의 객체를 참조하고 있으므로 객체의 값을 변경하면 두 참조자에 모두 반영되며 구조체는 두 변수(spt1,spt2)가 독립적인 객체를 가지므로 객체의 값을 변경하면 변경된 객체에만 반영됩니다.

 

클래스와 구조체 객체의 값 변경 예제입니다.

  1. using System;

    class CPoint

    {

      public int x, y;

      public CPoint(int _x, int _y)

      {

        x = _x;

        y = _y;

      }

      public CPoint()

      {

        x = 0;

        y = 0;

      }

      public void Print()

      {

        Console.WriteLine("class:({0},{1})", x, y);

      }

    } 

    struct SPoint

    {

      public int x, y;

      public SPoint(int _x, int _y)

      {

        x = _x;

        y = _y;

      }

      public void Print()

      {

        Console.WriteLine("struct:({0},{1})", x, y);

      }

    }

    class Program

    {

      static void Main()

      {

        CPoint cpt1 = new CPoint(1, 1);

        CPoint cpt2 = cpt1;

        cpt1.Print();

        cpt2.Print();

     

        cpt1.x = cpt1.y = 2;

        cpt1.Print();

        cpt2.Print();

        Console.WriteLine();

     

        SPoint spt1 = new SPoint(5, 5);

        SPoint spt2 = spt1;

        spt1.Print();

        spt2.Print();

     

        spt1.x = spt1.y = 6;

        spt1.Print();

        spt2.Print();

      }

    }

class:(1,1)
class:(1,1)
class:(2,2)
class:(2,2)

struct:(5,5)
struct:(5,5)
struct:(6,6)
struct:(5,5)

cpt1객체와 spt1객체를 변경한 출력입니다. cpt1은 cpt2와 같은 객체를 참조하므로 하나의 객체를 변경하면 두 참조자에 모두 값이 반영됩니다.

위 예제의 메모리 그림입니다.

참조와_값_복사2.PNG 

해서 참조 기반 형식을 값 복사(깊은 복사) 하려면 인터페이스를 사용해야 합니다. 요건 담에...

 

3, int의 복사

 int는 값 기반 형식입니다. 기본 형식에 대한 정리는 다음에...

 

c#의 기본 형식 => http://msdn.microsoft.com/ko-kr/library/ya5y69ds.aspx

위 표에 나와 있는 형식 중 object와 string을 제외한 모든 형식은 값 기반 형식입니다.

위 내용처럼 모든 C#의 수치형 형식은 모두 구조체(값 기반 형식)입니다.

 

다음은 int 변수(객체)의 초기화입니다.

  1. using System;

    class Program

    {

      static void Main()

      {

        int n1 = 0;

        int n2 = new int();

        int n3 = new System.Int32();

     

        Console.WriteLine("{0} {1} {2}", n1, n2, n3);

      }

    }

 0 0 0

 int 는 System.Int32 구조체의 별칭이며 기본 생성자에서 0으로 초기화됩니다. 기본 값 => http://msdn.microsoft.com/ko-kr/library/83fhsxwc.aspx

 

모든 수치형은 값 기반 형식이므로 아래처럼 복사됩니다.

  1. using System;

    class Program

    {

      static void Main()

      {

        int n1 = 10;

        int n2 = n1; //1.

        Console.WriteLine("{0} {1}", n1, n2);

        n1 = 20; //2.

        Console.WriteLine("{0} {1}", n1, n2);

        n2 = 30; //3.

        Console.WriteLine("{0} {1}", n1, n2);

      }

    } 

 10 10
20 10
20 30

다른 수치형도 이처럼 동작하겠죠?

 아래는 간단한 메모리 그림입니다.

int의_복사_동작.PNG 

 참고 :  http://msdn.microsoft.com/ko-kr/library/t63sy5hs(v=vs.90).aspx


반응형