[C++] 얕은 복사와 깊은 복사

WestCoast·2022년 1월 19일
1

C, C++

목록 보기
4/12

개념


  1. 객체 복사(Object Copy)
    기존의 객체의 사본을 생성하는 것.
    예를 들어 A 객체를 B 객체로 복사하는 것이다. 가장 흔한 것으론 복사 생성자 등을 통해서 Widget B = A; 와 같이 B에 A를 복사해줄 수 있다.
    하지만, 문제점이 존재한다.
    단순한 경우 복사는 새로운, 초기화되지 않은 객체(B)를 할당하여 원본 객체(A)의 모든 필드 속성을 복사함으로써 이루어진다.
    복잡한 경우(참조 값을 복사하는 경우) 이는 원하는 동작대로 결과가 나오지 않게 된다.
    이러한 차이로 인해 얕은 복사와 깊은 복사의 개념이 등장하게 되는 것이다.

<얕은 복사와 깊은 복사를 간략히 보여주는 예>
왼쪽의 얕은 복사는 p와 q가 하나의 메모리를 가리키게 된다.
오른쪽의 깊은 복사는 p와 q가 서로 다른 메모리를 가지도록 복사된다.
여기서 p, q는 포인터라고 생각해도 무방하다.

  1. 얕은 복사(Shallow Copy)

    • Widget B = A; 새 객체 B가 생성되며 A의 필드 값들은 B로 복사된다.
    • 필드 값이 포인터 등일 경우(char* 등) 참조(주소값)를 복사하므로 A와 B가 동일한 메모리 주소를 참조(공유)한다.
      이렇게 참조된 객체는 공유되므로 A나 B 중 하나가 수정되면 다른 쪽에서도 변경 사항이 보이게 된다.(같은 메모리 주소를 공유하므로)
    • 필드 값이 원시 자료형(int, float 등)인 경우 원시 자료형의 '값'을 복사한다. 그러므로 문제가 없다.
    • 얕은 복사는 단순하고 일반적으로 비용이 적다.
  2. 깊은 복사(Deep Copy)

    • 얕은 복사의 대안으로 깊은 복사가 있다.
    • 새로운 자료공간을 구성한 후 내용을 복사한다.
    • strcpy()와 같이, 별도의 메모리 영역을 확보(malloc)하고 내용을 복사하는 경우이다.
    • 얕은 복사는 디폴트 복사 생성자로 충분하다. 하지만 깊은 복사는 복사 생성자를 재정의해줄 필요가 있다.
    • 깊은 복사는 추가 객체의 생성이 필요하기 때문에 비용이 더 많이 든다.

예시


  1. 얕은 복사(Shallow Copy)
    디폴트 복사 생성자는 아래와 비슷하게 동작하고 있을 것이라고 짐작이 가능하다.
    name에 얕은 복사(rhs.name의 주소값을 넣어줌)가 일어나고 있다.
    결과적으로 의도한 바와 다르게 name 이 rhs.name 의 '주소'를 참조하고 있다.
    어떤 이유에서 rhs 객체가 소멸한다면 name 이 가리키는 주소는 더이상 유효하지 않게 될 것이다.
class ShallowCopyExam
{
public:
    int age;
    char* name;
    
    ShallowCopyExam(const ShallowCopyExam& rhs)
    {
        age = rhs.age;
        name = rhs.name;
    }
    ...
};    
  1. 깊은 복사(Deep Copy)
    깊은 복사를 위해 복사 생성자를 직접 작성해준다.
    strcpy() 메소드를 통해서 단순히 name에 rhs.name의 주소값을 넣어주지 않고, rhs.name을 임시 객체에 복사한 뒤 임시 객체의 '값(문자)'들을 name에 하나씩 복사해주고 있다.
class DeepCopyExam
{
public:
    int age;
    char* name;
    
    DeepCopyExam(const DeepCopyExam& rhs)
    {
        age = rhs.age;
        name = new char[strlen(rhs.name) + 1];
        // 단순히 주소값을 넣어주는 것이 아니라
        // rhs.name의 값을 name에 복사하고 있다.
        strcpy(name, rhs.name);
    }
    ...
};

예제 코드


#include <iostream>
#pragma warning(disable:4996)

using namespace std;

class ShallowCopyExam
{
public:
    int age;
    char* name;

    ShallowCopyExam(int _age, const char* _name)
    {
        age = _age;
        name = new char[strlen(_name) + 1];
        strcpy(name, _name);
    }

    /*
    * 디폴트 복사 생성자는 아래와 비슷하게 동작하고 있을 것이라고 짐작이 가능하다.
    * name에 얕은 복사(rhs.name의 주소값을 넣어줌)가 일어나고 있다.
    * 결과적으로 의도한 바와 다르게 name 이 rhs.name 의 '주소'를 참조하고 있다.
    * 어떤 이유에서 rhs 객체가 소멸한다면 name 이 가리키는 주소는 더이상 유효하지 않게 될 것이다.
    ShallowCopyExam(const ShallowCopyExam& rhs)
    {
        age = rhs.age;
        name = rhs.name;
    }
    */

    void infoPerson() 
    {
        cout << "이름: " << name << ", ";
        cout << "나이: " << age << endl;
    }
};

class DeepCopyExam
{
public:
    int age;
    char* name;

    DeepCopyExam(int _age, const char* _name)
    {
        age = _age;
        name = new char[strlen(_name) + 1];
        strcpy(name, _name);
    }

    // 복사 생성자 - 깊은 복사
    // 깊은 복사를 위해 복사 생성자를 직접 작성해준다.
    DeepCopyExam(const DeepCopyExam& rhs)
    {
        age = rhs.age;
        name = new char[strlen(rhs.name) + 1];
        // 단순히 주소값을 넣어주는 것이 아니라
        // rhs.name의 값을 name에 복사하고 있다.
        strcpy(name, rhs.name);
    }

    void infoPerson()
    {
        cout << "이름: " << name << ", ";
        cout << "나이: " << age << endl;
    }
};

int main()
{
    // ---------------------얕은 복사---------------------

    ShallowCopyExam human3(30, "세종");

    ShallowCopyExam human4 = human3;    // 디폴트 복사 생성자가 호출된다.
    human4.age = 33;
    strcpy(human4.name, "단군");

    cout << "<얕은 복사>" << endl;
    human3.infoPerson();    // 이름: 단군, 나이: 30
    human4.infoPerson();    // 이름: 단군, 나이: 33

    // ---------------------깊은 복사---------------------

    DeepCopyExam human1(20, "홍길동");

    DeepCopyExam human2 = human1;   // (깊은) 복사 생성자가 호출된다.
    human2.age = 22;
    strcpy(human2.name, "이순신");

    cout << "<깊은 복사>" << endl;
    human1.infoPerson();    // 이름: 홍길동, 나이: 20
    human2.infoPerson();    // 이름: 이순신, 나이: 22
}

출력

<얕은 복사>
이름: 단군, 나이: 30
이름: 단군, 나이: 33
<깊은 복사>
이름: 홍길동, 나이: 20
이름: 이순신, 나이: 22

참고

위키피디아 - 객체 복사
제타 위키 - 얕은 복사, 깊은 복사
모두의 코드 C 언어 레퍼런스 - strcpy 함수

profile
게임... 만들지 않겠는가..

0개의 댓글