호우동의 개발일지

Today :

article thumbnail

썸네일


매개변수로의 구조체와 클래스 사용

얕은 복사와 깊은 복사에 대해 알기 전에
구조체와 클래스에 대해 이야기해보려고 한다.

// 클래스 Test_c
class Test_c
{
    public int a;
    public int b;
}

// 구조체 Test_s
struct Test_s
{
    public int a;
    public int b;
};

둘 다 int형 변수 a, b를 담고 있는 변수형이다.
기능으로는 큰 차이가 없어 보인다.


그럼 이렇게 변수를 한 곳에 모아두는 것으로만 사용할 때는
사실 큰 차이가 없는 것일까?

 


다른 계산 결과가 나오는 예시 코드

그렇지 않다. 아래의 예시를 보자.

using System;
namespace CSharp_Test
{
    class Test_c
    {
        public int a;
    }

    struct Test_s
    {
        public int a;
    };

    public class EmptyClass
    {
        // 클래스형 변수를 받아 a 값에 더함
        static void AddClass(int x, int y, Test_c result)
        {
            result.a = x + y;
        }

        // 구조체형 변수를 받아 a 값에 더함
        static void AddStruct(int x, int y, Test_s result)
        {
            result.a = x + y;
        }

        public static void Main(string[] args)
        {
            Test_c p1 = new Test_c(); // 클래스형 변수 생성
            p1.a = 0; // a = 0 으로 초기화
            AddClass(1, 2, p1);
            Console.WriteLine("Class P1 = " + p1.a + "\n");

            Test_s p2 = new Test_s(); // 구조체형 변수 생성
            p2.a = 0;
            AddStruct(1, 2, p2);
            Console.WriteLine("Struct P2 = " + p2.a + "\n");
        }
    }	
}

<출력>

구조체에는 함수에서 계산한 값이 적용되지 않았다.
구조체는 함수에서 계산한 값이 적용되지 않았다.

똑같은 연산을 하는 함수에 다른 매개변수만 넘겼을 뿐인데,
결과가 다르게 나왔다.

클래스 변수는 함수에서 계산한 값이 그대로 메인 함수로 돌아와 출력되었고,
구조체 변수는 원래 있던 값 0이 출력되었다.

 


왜 이런 결과가 나올까?

이는 클래스는 기본적으로 참조형이고, 구조체는 기본형이기 때문이다.

참조형은 실제 값은 힙(Heap)에 저장되어 있고 주소를 가리킨다.

매개변수로 참조형 변수를 복사해도 변수가 가리키는 주소는 실제 참조형 변수와 같기 때문에
복사한 값이 수정되면 실제 값도 수정되어 버리는 것이다.

구조체는 기본형은 실제 값이 스택(Stack)에 저장되고
매개변수로 사용될 때 값 그 자체를 복사한다.

그렇기에 복사한 값이 수정되더라도 실제 값은 수정이 되지 않는 것이다. 
그래서 구조체와 클래스는 엄연히 차이가 생기는 것이다.

 


참조형 변수(클래스)를 사용할 때 주의할 점

참조형 변수의 이러한 특징 때문에 사용할 때 주의할 점이 있다.

using System;
namespace CSharp_Test
{
    class Test_c
    {
        public int a;
        public int b;
    }

    public class EmptyClass
    {

        public static void Main(string[] args)
        {
            Test_c p1 = new Test_c();
            p1.a = 0;
            p1.b = 2;
            
            // 클래스 변수 p2를 만들어 p1의 값을 그대로 가져올려고 함
            Test_c p2 = p1;
            
            // 가져온 뒤 p2의 a와 b 값을 변경
            p2.a = 10;
            p2.b = -1;

            Console.WriteLine("p1.a : " + p1.a+"\n");
            Console.WriteLine("p1.b : "+ p1.b+"\n");

        }
    }
}

<출력>

p1의 값도 바뀌는 것을 확인할 수 있다.
p1 변수의 값이 바뀌는 것을 확인할 수 있다.

이상하게 우리가 바꾼 것은 p2의 값인데, p1의 값 또한 바뀌었다.

이는 Test_c p2 = p1이 p2와 p1이 가리키는 주소를 같게 만들어주어,
사실상 두 변수가 같은 클래스라고 정의해 준 것과 다름없는 것이기 때문이다.

그렇기 때문에 서로 다른 인스턴스(클래스형 변수)를 만들고 싶다면 아래와 같이 해줘야 한다.

using System;
namespace CSharp_Test
{
    class Test_c
    {
        public int a;
        public int b;
    }

    public class EmptyClass
    {

        public static void Main(string[] args)
        {
            Test_c p1 = new Test_c();
            p1.a = 0;
            p1.b = 2;
            Test_c p2 = new Test_c();
            p2.a = 10;
            p2.b = -1;


            Console.WriteLine("p1.a : " + p1.a+"\n");
            Console.WriteLine("p1.b : "+ p1.b+"\n");

        }
    }
}

<출력>

이번에는 p1의 값이 수정되지 않은 것을 확인할 수 있다
이번에는 p1의 값이 수정되지 않은 것을 확인할 수 있다.

위와 같은 방식으로 new 키워드를 사용하여 직접 생성하던가
아니면 깊은 복사 방식을 사용하면 되는데,

깊은 복사에 대해서는 나중에 깊은 복사 vs 얕은 복사 포스팅에서 다루겠다.