[C++] ディープコピーとシャローコピー

はじめに: C++ では、オブジェクトが代入またはコピーによって別のオブジェクトのコピーになろうとしているときに、「浅いコピー」または「深いコピー」が発生します。

浅いコピーとは何ですか?

        浅いコピーとは、2 つのオブジェクトが同じメモリ アドレスを共有すること、つまりソース オブジェクトのデータ メンバのアドレスがターゲット オブジェクトに割り当てられることを意味し、ソース オブジェクトとターゲット オブジェクトが破壊されると、繰り返し解放の問題が発生します。 、プログラムがクラッシュする原因となります。

同じ型のオブジェクトを割り当てることができるため、2 つのオブジェクトのメンバー変数の値は同じであり、2 つのオブジェクトは依然として 2 つの独立したオブジェクトのままです。この状況は、浅いコピー        と呼ばれます

        通常の状況では、浅いコピーには副作用はありませんが、クラス内にポインタがあり、そのポインタが動的に割り当てられたメモリ空間を指している場合、デストラクタは動的メモリ解放処理を実行するため、メモリの問題が発生します。

 写真を見て理解してください。

以下は浅いコピーの例です。

class Person {
public:
    int mAge;
    char* pName;
    Person(const char* n, int a) : name(new char[strlen(n) + 1]), mAge(a) {
        strcpy(pName, n);
    }

    // 浅拷贝构造函数
    Person(const Person & p) : age(p.mAge), name(p.pName) {} 
};

        上記のコードでは、  ポインター メンバー pName の型と メンバー mAge の型Person を含むクラス を定義します 。シャロー コピー コンストラクターでは、ソース オブジェクトのアドレスがターゲット オブジェクトに直接割り当てられるため、2 つのオブジェクトが同じメモリを共有します。一方のオブジェクトが破棄されると、もう一方のオブジェクトのポインター メンバー変数がポイントします。メモリの一部が解放されたため、プログラムがクラッシュしました。charint

ディープコピーとは何ですか?

        ディープ コピーとは、オブジェクトがコピーされるときに、データを保存するために新しいメモリ空間が開かれ、ソース オブジェクトのデータ メンバーの内容がターゲット オブジェクトのデータ メンバーにコピーされることを意味します。繰り返しリリースされる問題。

        クラス内にポインターがあり、そのポインターに動的に領域が割り当てられ、デストラクターが解放処理を実行する場合、多くの場合、コピー コンストラクターをカスタマイズし、ポインターに動的に領域を割り当て、ディープ コピーを作成する必要があります

写真を見て理解してください。

以下はディープコピーの例です。

class Person {
public:
    int mAge;
    char* pName;
    Person(const char* n, int a) : name(new char[strlen(n) + 1]), mAge(a) {
        strcpy(pName, n);
    }

    // 深拷贝构造函数
    Person(const Person & p) : age(p.mAge), name(new char[strlen(p.pName) + 1]) {
        strcpy(name, p.pName);
    } 
};

        上記のコードでは、ディープ コピーを使用してオブジェクトを作成し、ソース オブジェクトのデータ メンバーの内容をターゲット オブジェクトにコピーすることで、繰り返しリリースされる問題を回避しています。

どのように選ぶか?

        オブジェクトに基本データ型のメンバーのみが含まれている場合は、浅いコピーを使用できますが、オブジェクトにポインター型のメンバーが含まれている場合は、ディープ コピーを使用する必要があります。

        実際には、特定の状況に応じてさまざまなコピー方法を選択する必要があります。オブジェクトにポインタ型のメンバーが存在する場合、コピー時に同じアドレスが共有されないように注意する必要があります。そうでない場合は、ディープ コピーを使用する必要があります。さらに、ディープ コピーを使用する場合は、メモリ リークを避けるためにメモリの解放にも注意する必要があります。

        以下は、ポインター型のメンバー変数を含む例です。

class Employee {
public:
    Employee(const char* n, int a, const char* p) : name(new char[strlen(n) + 1]), age(a), position(new char[strlen(p) + 1]) {
        strcpy(name, n);
        strcpy(position, p);
    }

    ~Employee() {
        delete[] name;
        delete[] position;
    }

    // 浅拷贝构造函数
    Employee(const Employee& other) : name(other.name), age(other.age), position(other.position) {} 

    // 深拷贝构造函数
    Employee(const Employee& other) : age(other.age), name(new char[strlen(other.name) + 1]), position(new char[strlen(other.position) + 1]) {
        strcpy(name, other.name);
        strcpy(position, other.position);
    }

private:
    char* name;
    int age;
    char* position;
};

        上記のコードでは、  2 種類のポインター型メンバー変数  と 、および 1  種類の基本データ型メンバー変数 の Employee 3 つのメンバー変数を含むクラス を定義しますcharnamepositionintage

        浅いコピーを作成したい場合は、ソース オブジェクトのポインタ型メンバ変数のアドレスをターゲット オブジェクトのポインタ型メンバ変数にコピーするだけでよいことがわかりますが、これには問題があります。ソースオブジェクトとターゲットオブジェクトはポインタが指すメモリ空間を共有します。ソースオブジェクトが破壊されると、ターゲットオブジェクトが保持していたポインタは解放されたメモリ空間を指し、プログラムがクラッシュします。したがって、浅いコピーはこのタイプのメンバー変数には適していません。

        ポインター型のメンバー変数を含むオブジェクトの場合、一般的に使用されるコピー方法はディープ コピーですこのコピー メソッドは、ターゲット オブジェクトにメモリのブロックを再割り当てし、ソース オブジェクトのデータ メンバーの内容をターゲット オブジェクトのデータ メンバーにコピーすることで、繰り返し解放される問題を回避します。上記のコードでは、ディープ コピー メソッドを使用してターゲット オブジェクトを作成します。

要約:

        C++の開発において、ディープコピーとシャローコピーはどちらも非常に重要な概念であり、具体的には、オブジェクトのメンバ変数の種類に応じてコピー方法を選択すること、ディープコピーを使用する場合にはメモリ解放の問題に注意する必要があります。コピー。オブジェクトにポインター型メンバーが含まれる場合はディープ コピーを使用することをお勧めします。オブジェクトに基本データ型メンバーのみが含まれる場合は浅いコピーを使用できます。

おすすめ

転載: blog.csdn.net/crr411422/article/details/131033612