[C#] 클래스(Class) / 구조체와 클래스의 차이 / 스택 메모리와 힙 메모리 / 상속(inheritance)

2023. 8. 9. 17:02C#

728x90
반응형

구조체와 클래스는 비슷하지만 메모리 구조가 다르다. 클래스는 객체를 관리하기 위해 사용된다. 레퍼런스 변수가 있으며 실제 데이터는 레퍼런스가 가리키는 형식이다. 클래스의 실제 데이터는 힙 영역에 있다.

(**함수 안의 값 형식 메모리는 스택에 존재 / 함수 밖의 값 형식 메모리는 힙에 존재)

 

 

public class TestScript : MonoBehaviour
{
    public class ClassPos
    {
        public int x;
        public int y;
        public int z;
        public void Show()
        {
            print(x + " " + y + " " + z);
        }
        void Start()
        {
            ClassPos cPos;
            cPos.x = 1;
            cPos.Show();
        }
    }
}

구조체와 다르게 위와 같이 선언하고 대입하면 cPos.x = 1; 부분에서 오류가 발생한다. (할당되지 않은 cPos 지역변수를 사용했습니다) cPos는 레퍼런스 변수이다.

 

 

public class TestScript : MonoBehaviour
{
    public class ClassPos
    {
        public int x;
        public int y;
        public int z;
        public void Show()
        {
            print(x + " " + y + " " + z);
        }
    }
    void Start()
    {
            ClassPos cPos = new ClassPos();
            cPos.x = 1;
            cPos.Show();
    }
}

오류가 없어지고 값 "1, 0, 0"이 출력된다.

 

 


 

 

클래스에 추가 생성자 사용

-기본 생성자는 추가하지 않아도 존재한다. 구조체와 다른 점은 레퍼런스로 동작하고 실제 메모리는 다른 곳(힙 메모리 영역)에 생긴다는 점이다. 

public class TestScript : MonoBehaviour
{
    public class ClassPos
    {
        public int x;
        public int y;
        public int z;
        public ClassPos(int x, int y, int z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }
    }
    void Start()
    {
            ClassPos cPos = new ClassPos(10, 20, 30);
            print(cPos.x + " " + cPos.y + " " + cPos.z);
    }
}

 

 


 

클래스 예제 1

public class Test : MonoBehaviour
{  
    public class ClassPos
    {
        public int x;
        public int y;
        public int z;
        public void Show()
        {
            print(x + " " +  y + " " + z);
        }
    }
    void Start()
    {
        ClassPos cPos1 = new ClassPos();
        cPos1.x = 1;
        cPos1.y = 2;
        cPos1.z = 3;
        ClassPos cPos2 = cPos1;
        cPos1.Show();
        cPos2.Show();
    }
}

ClassPos 클래스를 생성한 후 new ClassPos(); 로 클래스를 초기화한다. cPos1 레퍼런스 스택 메모리가 생성된다. 실데이터에 값이 대입되고 cPos2도 cPos1의 레퍼런스 값을 그대로 받아 출력값이 같게 나오게 된다. cPos1, cPos2의 레퍼런스 스택 메모리는 출력 후에 사라지게 된다. 함수가 끝난 후에는 힙 메모리 값이 어디에도 참조되어 있지 않기 때문에 가비지 콜렉터가 작동하여 힙에 있는 실데이터 x, y, z 값을 제거한다.

 

 


 

 

클래스 예제 2

public class Test : MonoBehaviour
{  
    public class ClassPos
    {
        public int x;
        public int y;
        public int z;
        public void Show()
        {
            print(x + " " +  y + " " + z);
        }
    }
    void Start()
    {
        ClassPos cPos1 = new ClassPos();
        ClassPos cPos2 = new ClassPos();
        cPos1.x = 1; cPos1.y = 2; cPos1.z = 3;
        cPos2.x = 10; cPos2.y = 20; cPos2.z = 30;
        cPos1.Show();
        cPos2.Show();
    }
}

cPos1, cPos2의 레퍼런스 스택 메모리가 각각 생성된 후 해당 레퍼런스 각각의 힙 메모리 실데이터에 값이 입력되며 출력도 각각의 실데이터 값으로 나온다.

 

 


 

 

ClassPos cPos;
void Start()
{
    cPos = new ClassPos(1, 2, 3);
    cPos.Show();
}

위와 같이 레퍼런스 변수 cPos가 함수 바깥에서 선언되었다면, 스택 메모리가 아닌 힙 메모리로 생성되게 된다 실제 메모리도 힙 메모리에 생성된다. 함수 내부에서 레퍼런스 변수를 선언하면 함수가 끝날 때 가비지 컬렉터가 다 지우기 때문에 보통은 바깥에서 선언하는 경우가 많다.

 

 


 

 

상속(inheritance)

상속을 받으면 상위 클래스의 기능을 전부 받아서 쓸 수 있다.

public class Test : MonoBehaviour
{  
    public class Parent
    {
        public int x;
    }
    public class Child : Parent
    {
        public int y;
    }
    void Start()
    {
        Child c = new Child();
        c.x = 1;
        c.y = 2;
        print(c.x + " " + c.y);
    }
}

위의 예제를 보면, x, y 변수 전부 Child에서 쓸 수 있다.

 

 

public class PlayerScript : MonoBehaviour
{
    int speed;
    int power;
    void Start()
    {
        print(speed + " " + power);
    }
}

즉, 유니티 안에서 쓰는 MonoBehaviour는 우리가 해당 클래스를 상속받아서 모든 기능을 쓰겠다는 의미이고 Start() 함수도 사용하고 스크립트로도 쓸 수 있는 것이다.

728x90
반응형