[C++]浅い放射と深い放射


コンセプト

  • オブジェクトのコピー(オブジェクトコピー)
    既存のオブジェクトのコピーを作成します.
    たとえば、AオブジェクトをBオブジェクトにコピーします.最も一般的なのは、Widget B = A;のようなコピージェネレータなどによってAをBにコピーすることができることである.
    しかし、問題もある.
    簡単に言えば、コピーは、元のオブジェクト(A)のすべてのフィールド属性をコピーするために、初期化されていない新しいオブジェクト(B)を割り当てることによって実現される.
    複雑な場合(参照値がコピーされている場合)、結果は必要な操作に合致しません.
    これらの違いにより、浅いコピーと深いコピーの概念が現れます.
  • <浅いレプリケーションと深いレプリケーションの例>
    左の浅いコピー、pとqはメモリを指します.
    右側の深いコピーは、pとqの異なるメモリをコピーします.
    ここではp,qをポインタとしてもよい.

  • 浅いコピー
  • Widget B = A;は、新しいオブジェクトBを作成し、Aのフィールド値をBにコピーします.
  • フィールド値がポインタ等(char*等)の場合、参照(アドレス値)がコピーされるので、AとBは同じメモリアドレスを参照(共有)する.
    このように参照されるオブジェクトは共有され、AまたはBのいずれかが変更されると、もう一方も変更されます.(同じメモリアドレスを共有)
  • フィールド値が元のデータ型(int、floatなど)の場合、元のデータ型の「値」がコピーされます.したがって問題ありません.
  • 浅いコピーは簡単で、一般的なコストは低い.

  • 深くコピー
  • 浅いコピーの代替案は、深いコピーである.
  • 新しいデータ空間を作成し、コンテンツをコピーします.
  • strcpy()と同様に、独立したメモリ領域(malloc)を取得し、コンテンツをコピーします.
  • 浅い放射は双極子放射を生成するのに十分である.ただし、深度レプリケーションでは、レプリケーション作成者を再定義する必要があります.
  • 深度コピーは、他のオブジェクトを作成する必要があるため、よりコストがかかります.
  • 浅いコピー(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;
        }
        ...
    };    
  • 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

    リファレンス


    Wikipedia-複製対象
    ゼタウィキ-浅い放射、深い放射
    すべてのコードC言語参照-strcpy関数