2015. 2. 3.

[C++]복사 생성자(Copy Constructor) 호출 시점

복사 생성자와 깊은 복사(deep copy), 얕은 복사(shallow copy)에 대해서 알아보았다. 이전의 설명만 보면 복사 생성자는 특별한 경우에만 사용될 것처럼 느껴질 수 있다. 하지만 복사 생성자는 생각보다 빈번하게 호출된다. 이번에는 어떤 경우에 호출이 되길레 빈번하게 호출되는지에 대해서 알아보고자 한다.


복사 생성자의 호출 시점

 복사 생성자의 호출 시점은 크게 세 가지로 나눌 수 있다.

  1. 기존에 생성된 객체를 이용해서 새로운 객체를 초기화하는 경우
  2. call-by-value 방식으로 함수를 호출하는 과정에서 객체를 인자로 전달하는 경우
  3. 참조가 아닌 방식으로 객체를 반환하는 경우
 이 세 경우를 각각 살펴보자.


기존에 생성된 객체를 이용해서 새로운 객체를 초기화하는 경우

 이 경우는 이미 자주 봐온 경우이다. 먼저 코드로 보자.
 p2를 위한 메모리를 할당하고, 복사 생성자를 호출하여 p1의 멤버 변수에 대해 깊은 복사를 실행한다. 복사 생성자를 설명할 때 자주 등장한 내용이므로 더이상의 설명은 생략한다.


call-by-value 방식으로 함수를 호출하는 과정에서 객체를 인자로 전달하는 경우

 간단하게 말해 객체를 함수의 인자로 전달하는 경우이다.
 인자로 전달된 객체가 어떻게 처리되는지 먼저 알아볼 필요가 있다. Func() 함수의 인자로 p1이 전달되었다. 하지만 p1 자체가 전달된 것은 아니다. p1를 전달한 것이 아니라 p1를 복사할 객체를 하나 생성하고, 그 객체에 p1를 복사한 것이다. 좀더 자세하게 설명하자면 p1를 복사할 수 있는 객체를 생성하기 위해 메모리를 할당한다. 그리고 정의된 복사 생성자에 의해 준비된 메모리에 p1의 값을 복사하여 초기화한다. 위의 Person 클래스는 디폴트 복사 생성자가 아니라 깊은 복사가 이뤄질 수 있도록 정의된 복사 생성자이다. 복사가 끝난 객체 per이 함수에 최종적으로 전달되어 함수가 실행된다. 이런 방식이 바로 call-by-value이다. 즉 객체를 함수의 인자로 전달하면 새로운 객체가 복사 생성자의 의해 생성된다. 만약 반환값이 없다면 이 객체는 함수를 빠져나오는 즉시 자동으로 소멸된다.


참조가 아닌 방식으로 객체를 반환하는 경우

 방금 객체를 반환하는 경우에 대한 이야기를 살짝 하였다. 이번에는 이에 대해 자세히 살펴보자.
 바로 앞 Func() 함수에 객체를 반환하도록 수정하였다. 객체를 반환하는 경우 per를 그대로 반환할 수 있는지 생각해보자. 앞서 언급한대로 함수를 빠져나오면 지역변수인 per를 소멸한다. 소멸된 메모리 공간에 대해 반환한다는 자체가 말이 되지 않는다. per는 함수 종료와 함께 소멸된다. 반환값이 메모리 상에 저장되어야 p2에 복사할 수 있다. 그래서 반환할 때 per을 복사한 새로운 객체가 생성될 수밖에 없다. per는 소멸되고, per를 복사한 새로운 객체, 즉 임시객체가 복사 생성자를 호출하여 만들어 진다. 그리고 반환되는 값은 이 임시객체를 참조하는 참조값이 반환된다. 추가적으로 객체 p2 역시 메모리에 할당되고 앞서 언급한 임시객체에 대해 복사 생성자를 호출하여 p2값을 초기화한다.

 그러면 반환된 객체를 참조하는 경우 임시객체는 어떻게 될까?
 이 경우 임시객체는 소멸되지 않는다. 참조자에 의해 참조되는 임시객체를 메모리 공간에 계속 해서 존재한다. 만약에 메모리에 존재하지 않고 사라지게 되면 p2는 임시객체가 이미 사라진 공간을 참조하게 되므로 문제가 발생할 것이다. 그래서 참조자에 의해 참조된 임시객체는 사라지지 않는 것이다.

댓글 없음:

댓글 쓰기