C++深いコピーと浅いコピー


通常のタイプのオブジェクトでは、int a=88などのコピーは簡単です.int b=a; double f=3.12; double d(f);
一方、クラスオブジェクトは通常のオブジェクトとは異なり、クラスオブジェクトの内部構造は一般的に複雑で、様々なデータメンバーが存在します.
#include   
using namespace std;  
class Test  
{  
private:  
    int a,b;  
public:  
    Test(int x, int y)     //       ,                
    {  
        a=x;  
        b=y;  
    }  
    Test(const Test& C)   //      ,               
    {  
        a=C.a;  
        b=C.b;  
    }  
    void show ()  
    {  
        cout<
                  ,       b c ,      a   。
          ,      ,                           。
        Test::Test(const Test& C),              。  ,                ,             ,                    ,    const  ,                         。
                                       ,             。    ,          ,           。              :

1つのオブジェクトが値伝達で関数体に入力され、1つのオブジェクトが値伝達で関数から1つのオブジェクトに戻るには、別のオブジェクトで初期化する必要があります.クラスにレプリケーションコンストラクタが明示的に宣言されていない場合、コンパイラはオブジェクト間の浅いレプリケーションを完了するデフォルトのレプリケーションコンストラクタを自動的に生成します.後述します.カスタムレプリケーションコンストラクタは、コンパイラがデフォルトのレプリケーションコンストラクタを形成することを阻止し、ソースコードの効率を向上させる良いプログラミングスタイルです.
浅いレプリケーションと深いレプリケーションは、上述したコンストラクション関数で処理したように、データ・メンバーに直接値を付与すればよい.多くの場合、これは可能です.オブジェクトのデータ・メンバーにストレージ領域を割り当てる新しいオブジェクトを作成し、直接値を割り当てると、対応する空間に値を保存します.しかし、このような浅いレプリケーションは、天下を通行することはできません.次のプログラムでは、浅いレプリケーションが問題をもたらしています.
#include   
#include   
using namespace std;  
class Test  
{  
private:  
    int a;  
    char *str;  
public:  
    Test(int b, char *s)  
    {  
        a=b;  
        strcpy(str,s);  //    ,       
    }  
    Test(const Test& C)  
    {  
        a=C.a;  
        strcpy(str,C.str);  
    }  
    void show ()  
    {  
        cout<
         ,       :          。      ,       ,           。

main関数から見ます.
プログラムが28行目のTest a(100,「hello」)に実行されると、オブジェクトaのデータメンバーaは、実際のパラメータ100の値を取得し、データメンバーstr、すなわちポインタは、ランダムアドレス値(ポインタの値、ポインタが指す値ではない値)である!プログラムでは、strcpy(str,s)を通過しようとする.形式パラメータsが指す文字列「hello」をstrが指す位置にコピーし、その位置、そのアドレスはシステムによって割り当てられていない危険な操作です.ここでstrのように割り当てられていないアドレス(ポインタ)を「野ポインタ」と呼ぶ.
第29行Test b(a)を実行する.また、同じことが起こります.
strのような野の指針はどんなに覇道なのか.あるシステムでは、このような行為が受け入れられ、どれほど危険かが考えられる.一部のシステムでは、このようなことは許されません.すると、上記のプログラムがcodeblockでデバッグ中にエラーが発生します.これは間違って来たほうがいい.
このような問題を解決する方法は,コンストラクション関数でポインタタイプのメンバーに専用の空間を割り当てることである.このルールで構築されたレプリケーションは、深いレプリケーションと呼ばれます.
上のプログラムは、次のように書き換えられます.
#include   
#include   
using namespace std;  
class Test  
{  
private:  
    int a;  
    char *str;  
public:  
    Test(int b, char *s)  
    {  
        a=b;  
        str=new char[strlen(s)+1];  //  str     ,     s       。   1?       \0  
        strcpy(str,s);  //         ,            
    }  
    Test(const Test& C)  
    {  
        a=C.a;  
        str=new char[strlen(C.str)+1];   //  ,  str         
        strcpy(str,C.str);  
    }  
    ~Test()  
    {  
        delete []str;  
    }  
    void show ()  
    {  
        cout<

#include using namespace std; class A{private:int arrayAddr;//len個の整数要素を持つ配列のヘッダアドレスint lenを保存する//動的配列の長さpublic:A(int a,int n);~A();int sum()};A::A(int*a,int n){len=n;arrayAddr=new int[n]//ポインタデータメンバーにスペースを割り当てるが、上記の例で1を加えることなくfor(int i=0;i{arrayAddr[i]=a[i]}////解析関数のクラス外定義は,ポインタ型データaが指す空間A:~A(){delete[]arrayAddr;}を解放する.int A::sum()/aが指す配列のiと下の要素の値{int s=0;for(int i=0;i{s+=arrayAddr[i];return s; }
int main(){ int b[10]= {75, 99, 90, 93, 38, 15, 5, 7, 52, 4}; A r1(b,10); cout