Software Development/C, C++

[C++] 복사생성자의 깊은복사 & 얕은복사

huiyu 2018. 12. 29. 12:33

복사 생성자  : http://huiyu.tistory.com/341

*디폴트 복사 생성자는 멤버 대 멤버의 복사를 진행한다. 이러한 방식의 복사를 가리켜 '얕은 복사'라 하는데, 이는 멤버변수가 힙의 메모리 공간을 참조하는 경우에 문제가 된다.

*디폴트 복사 생성자의 문제점

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
#include <cstring>
 
using namespace std;
 
class Person
{
private:
  char * name;
  int age;
public:
  Person(char * myname, int myage)
  {
    int len = strlen(myname) + 1;
    name = new char[len];
    strcpyt(name, myname);
    age = myage;
  }
 
  void ShowPersonInfo() const
  {
    cout  << "이름 : " << name << endl;
    cout  << "나이 : " << age  << endl;
  }
 
  ~Person()
  {
    delete []name;
    cout<<"called destructor!"<<endl;
  }
 
};
 
int main(void)
{
  Person man1("Lee dong woo"29);
  Person man2 = man1;
  man1.ShowPersonInfo();
  man2.ShowPersonInfo();
 
  return 0;
}
cs

[실행결과]
이름 : Lee dong woo
나이 : 29
이름 : Lee dong woo
나이 : 29
called destructor!

 -> 위의 실행결과에서 주목할 부분은 문자열 "called destructor!"가 단 한번만 호출되었다는 점이다.
두개의 객체를 생성했으므로, 소멸자도 두 번 호출되어야 하는데, 실행결과를 보면 한번만 호출되고 있다.

Person man2 = man1;

디폴트 복사 생성자는 멤버 대 멤버를 단순히 복사만 하므로, 다음의 구조를 띄게 된다.

->단순 복사를 진행하므로 하나의 문자열을 두 개의 객체가 동시에 참조하는 꼴을 만들고 있다.
 이로 인해 객체의 소멸과정에서 문제가 발생한다.

man2객체가 먼저 소멸된 상황을 가정해보자, man2가 소멸시에 소멸자가 호출되면서 다음 문장도 실행된다.

 delete [] name;

객체 man2의 소멸로 인해 다음과 같은 상황에 놓이게 된다.

이제 남아있는 man1객체가 소멸되면서 man1의 소멸자가 실행되어야 한다.
그러나 man1객체의 멤버 name이 참조하는 문자열은 이미 소멸된 상태이다. 조금 전 man2의 소멸자가 호출되면서 소멸시켜버렸다.

* 이미 지워진 문자열을 대상으로 delete 연산을 하기 때문에 문제가 된다. 따라서 복사 생성자를 정의할 땐 이러한 문제가 발생하지 않도록 신경써야한다.

*'깊은복사'를 위한 복사 생성자 정의

1
2
3
4
5
Person (const Person& copy) : age(copy.age)
{
  name = new char[strlen(copy.name)+1];
  strcpy(name, copy.name);
}
cs

 - 멤버 변수 age의 멤버 대 멤버 복사
 - 메모리 공간 할당후 문자열 복사, 그리고 할당된 메모리의 주소값을 멤버 name에 저장


[참고자료]
윤성우의 열혈 C++프로그래밍 05-2 '깊은 복사'와 '얕은 복사'

728x90