클래스 변수 문의드립니다.

이곳에 질문들을 보다 느낀게 뭔가 체계적으로 공부을 못해서인지 기본이 너무 부족한 느낌이 들어 기본 예제를 다시보고 있는데 궁금한게 있어 질문을 드립니다.

객체를 매개변수로 넘겨줄때 참조형식으로 넘겨주는건 알겠는데 Test_D 클래스 안의 new_C 클래스 변수에만 값을 적용해보고 싶은데 new 키워드로 생성해도 마찬가지로 변경이 되어버리네요.
기존의 값을 전달받아 해당 객체에만 값을 변경하고 싶은데 안되는건가요??

       Test_C c = new Test_C();
        c.Print_C(); //1000

        Test_D d = new Test_D(c);
        d.Print_D(); //1000

        c.set_C(5000);
        c.Print_C(); //5000
        d.Print_D(); //5000
        d.set_D(8000);
        c.Print_C(); //8000
        d.Print_D(); //8000


class Test_C 
{
 
    int C = 1000;

    public void Print_C() 
    {

            Console.WriteLine(C);
         
    }

    public void set_C(int new_Value) 
    {
        C = new_Value;
    
    }


}

class Test_D 
{
    Test_C new_C;

    public Test_D(Test_C old) 
    {
        new_C = new Test_C();
        new_C = old; //참조주소를 가지게 된다.
    
    }

    public void Print_D() 
    {
        new_C.Print_C();
    }

    public void set_D(int a) 
    {
        new_C.set_C(a);
    
    }
}
1개의 좋아요

새로운 인스턴스를 생성해서 할당 하였지만,
다시 참조 형태로 받은 old를 할당 하였으니

동일하게 변경 됩니다.

3개의 좋아요

안녕하세요. 방금 사이트 처음 가입해서 첫화면에 이글이 보여서 들어왔다가 도움이 되실까 싶어서 첫 글을 작성합니다.

public Test_D(Test_C old) 
    {
        new_C = new Test_C();
        new_C = old; //참조주소를 가지게 된다.
    
    }

쓴이분이 작성하신 위의 코드를 보면
“new_C = new Test_C();” 이 줄은 아무의미가 없습니다. 바로 다음줄에 의해서
new_C는 전달인자로 받아온 old라는 애를 참조하니까요.
좀 더 과감히 추측성 발언을 하자면 Test_C() 생성자가 별다른 행위를 하지 않는 생성자이기도 해서
저 줄이 아무의미 없는 정도가 아니라 컴파일러에 의해서 아예 삭제되는 줄이 될수도 있을 것 같아요.
그래서 “new_C = old;” 이 줄을 지우면 원하시는대로 새로 생성한 C 객체를 D가 들고 있겠지만
그렇다면 전달인자도 필요가 없겠죠?
그냥 언어 학습을 위해서 이것저것 해보시는 중인 것 같아요. 충분히 초보라면 개념들이 바로 서지 않아
혼란한 와중에 이런 생각과 질문을 할 수 있어요.

1개의 좋아요

Test_D 클래스 생성자 내부에서 Test_C객체를 '복사’하는 로직을 추가해보세요. 혹은 ‘복사된’ 객체를 넣든가욥.

1개의 좋아요

안녕하세요. 그간 저런식으로 지역변수를 선언해 따로 뭔가를 작업하는 경우가 없었는데 예제를 보다가 급 궁금해져서 ㅎㅎ;; 객체를 복사하는 방법이 별도로 있나했습니다. 그냥 새로 생성해서 값을 전부 대입해주는 방법밖에 없네요

1개의 좋아요

안녕하세요. 말씀하신바와 같이 아리까리한 부분을 이번 기회에 확실하게 개념을 잡고 가려고 하네요. 클래스는 복사는 안되는거 같네요. 전부다 값을 대입해줘야 하나봅니다 ㅎㅎ

1개의 좋아요

안녕하세요. 클래스 복사는 전부다 값을 대입해줘야 하나보군요.

1개의 좋아요

그래도 되는데, 직렬화한 뒤, 그걸 역직렬화하여 복사하는 방법도 있습니다.

1개의 좋아요

아마 아래와 같은 동작을 기대하셨던 듯 합니다.

struct Val
{
    public int Value;
}

class C
{
    Val _v;
    public C(Val v) => _v = v; 

    public void PrintVal() => Console.WriteLine(_v.Value); 
    public void SetVal(int value) => _v.Value = value;
}

var v = new Val();
var c = new C(val);

Console.WriteLine(v.Value); // 0
c.PrintVal(); // 0

v.Value = 1000;

Console.WriteLine(v.Value); // 1000
c.PrintVal(); // 0

c.SetVal(500);

Console.WriteLine(v.Value); // 1000
c.PrintVal(); // 500

C# 에서 참조 자료형(class)은 참조값이, 값 자료형(struct)는 값- 인스턴스가 통째로 복사됩니다.

참조 자료형은 참조값만 복사되기 때문에 메모리 효율적이지만, GC를 유발하기 때문에 성능 비효율입니다. 이와 반대로, 값 자료형은 값이 복사되기 때문에 메모리 비효율적이지만 GC를 유발하지 않기 때문에 성능 효율적입니다.

이러한 특성을 살펴, 필요에 따라 선택할 수 있습니다.

참조값이 복사되는 행태를 "얕은 복사(Shallow Copy)"라고 부르며, 값(인스턴스) 전체가 복사되는 행태를 "깊은 복사(Deep Copy)"라고 부릅니다.

그런데, 참조 자료형에게 깊은 복사 행태를 부여할 때는 꽤 많은 사항이 고려되어야 합니다.

만약 C 가 아래와 같이 정의되어 있다면,

class C
{
    Val _v; // 갑 자료형.
    public C MyC; // 참조 자료형

    public C(Val v) => _v = v; 

    public void PrintVal() => Console.WriteLine(_v.Value); 
    public void SetVal(int value) => _v.Value = value;

    public C DeepCopy()
    {
        var newC = new(_v);
        // newC.MyC = MyC.DeepCopy(); // 무한 재귀 호출.
        // newC.MyC = MyC; // 얕은 복사
        if (MyC != null)
        {
            newC.MyC = MyC.DeepCopy();
        }

        return newC;
    }
}

순수한 딥카피를 하려면, MyC 에 할당된 객체의 값도 복사를 해야하는데, 이는 또 다시 그 내부의 MyC 에 할당된 값도 복사되어야 함을 의미합니다.

때로는 이 과정에서 무한 복사로 인한 메모리 소진 문제에 직면할 수 있고, 복사된 인스턴스로 인해 많은 양의 메모리가 소진될 수도 있습니다.

참고로, 위와 같은 메모리 소진 위험성 때문에, 무조건 값 복사를 하는 값 자료형은 아래와 같은 재귀적 정의를 언어 차원에서 허락하지 않습니다.

struct Val
{
    public int Value;
    public Val MyVal; // 컴파일 에러
}

종합해보면, 깊은 복사는 프로그래머의 필요와 사정에 의해 얕은 카피와 순수한 깊은 복사 사이 어디 쯤인가에서 타협을 해야 합니다. 이는 언어가 일률적으로 규정할 수 없는 부분이기에, 프로그래머의 재량으로 남겨 둘 수 밖에 없습니다.

참고로, object 클래스 수준에서 MemberwiseClone 메서드를 제공하는데,

Object.MemberwiseClone Method (System) | Microsoft Learn

이 메서드는 새로운 객체를 생성하여, 값 자료형 멤버는 값 복사를, 참조 자료형 멤버는 참조값 복사를 수행한 후 새로운 객체를 반환하기에, 별다른 위험이 없습니다.

3개의 좋아요