web-dev-qa-db-ja.com

c ++のデフォルトの代入演算子=は浅いコピーですか?

他のどこにも確かな答えを見つけることができなかった単純な簡単な質問。デフォルトのoperator =は、右側のクラスのすべてのメンバーの浅いコピーだけですか?

Class foo {
public:
  int a, b, c;
};

foo f1, f2;
...
f1 = f2;

と同じになります:

f1.a = f2.a;
f1.b = f2.b;
f1.c = f2.c;

これは私がテストしたときは正しいようですが、特定のケースを見逃していないことを確認する必要があります。

41

私は言うだろう、デフォルトoperator=copyです。各メンバーをコピーします。

シャローコピーとディープコピーの違いは、コピーされるメンバーがポインターなどのある種の間接でない限り発生しません。デフォルトではoperator=が懸念されます。「コピー」が何を意味するかは、コピーされるメンバー次第です。深い場合も浅い場合もあります。

具体的には、生のポインターをコピーしてもポインターの値がコピーされるだけで、参照先には何も行われません。したがって、ポインタメンバーを含むオブジェクトは、デフォルトで浅いコピーされますoperator=

コピー時にクローン操作を実行するスマートポインターの作成にはさまざまな取り組みがあります。そのため、生のポインターの代わりにどこでもこれらを使用すると、デフォルトのoperator=はディープコピーを実行します。

オブジェクトに標準のコンテナーがメンバーとして含まれている場合、(たとえば)Javaプログラマーがoperator=は「浅いコピー」です。 Javaの場合、Vectorメンバーは実際には単なる参照なので、「浅いコピー」とは、Vectorメンバーが複製されないことを意味します。ソースと宛先は、同じ基礎となるベクトルオブジェクト。C++では、メンバーは参照ではなく実際のオブジェクトであるため、vectorメンバーwillがその内容とともにコピーされます(およびvector::operator=は、コンテンツが一緒にコピーされることを保証します)。

データメンバーがポインターのベクトルである場合、深いコピーまたはの浅いコピーもありません。コピー元とコピー先のオブジェクトが別々のベクターを持っているセミディープコピーがありますが、それぞれからの対応するベクター要素は同じクローンされていないオブジェクトを指しています。

39
Steve Jessop

はい、デフォルトのoperator=は浅いコピーです。

ちなみに、クラスにがある場合、shallow copydeep copyactualの違いが表示されますメンバーフィールドとしてのポインター。ポインターがない場合、違いはありません(私の知る限り)。

それらの違いを知るには、(stackoverflow自体の)以下のトピックを参照してください。

15
Nawaz

はい、それはオブジェクトをメンバーごとにコピーするだけであり、これは生のポインターに問題を引き起こす可能性があります。

9
Mark B

"浅い"コピーと "深い"コピーは、CまたはJavaの場合よりもC++の場合ほど意味がありません。

これを説明するために、Fooクラスを3つのintsからintint*、およびvector<int>に変更しました。

#include <iostream>
#include <vector>

class Foo {
public:
  int a;
  int *b;
  std::vector<int> c;
};

using namespace std;

int main() {
  Foo f1, f2;
  f1.a = 42;
  f1.b = new int(42);
  f1.c.Push_back(42);
  f2 = f1;

  cout << "f1.b: " << f1.b << " &f1.c[0]: " << &f1.c[0] << endl;
  cout << "f2.b: " << f2.b << " &f2.c[0]: " << &f2.c[0] << endl;
}

このプログラムを実行すると、次の出力が生成されます。

f1.b: 0x100100080 &f1.c[0]: 0x100100090
f2.b: 0x100100080 &f2.c[0]: 0x1001000a0

intは退屈なので、省略しました。ただし、int*vector<int>の違いを見てください。int*はf1とf2で同じです。それはあなたが「浅いコピー」と呼ぶものです。ただし、vector<int>はf1とf2で異なります。これは、「ディープコピー」と呼ばれるものです。

実際にここで何が起こったかは、C++のデフォルトのoperator =が、すべてのメンバーのoperator =が順番に呼び出されたかのように動作することです。 ints、operator =s、およびその他のプリミティブ型のint*は、バイト単位の浅いコピーです。 operator =vector<T>は、ディープコピーを実行します。

だから私は質問への答えは、いいえ、C++のデフォルトの代入演算子は浅いコピーを実行しません。ただし、ディープコピーも実行されません。 C++のデフォルトの代入演算子は、クラスのメンバーの代入演算子を再帰的に適用します。

7

個人的には Accelerated c ++ の説明が好きです。テキスト(201ページ)は次のように述べています。

クラス作成者が代入演算子を指定しない場合、コンパイラはデフォルトバージョンを合成します。デフォルトのバージョンは、再帰的に動作するように定義されています-各データ要素を、その要素のタイプに適したルールに従って割り当てます。各クラスタイプのメンバーは、そのメンバーの割り当て演算子を呼び出すことによって割り当てられます。 組み込みタイプのメンバーは、それらの値を割り当てることによって割り当てられます。

質問のメンバーは整数であるため、深くコピーされていることを理解しています。次の例の適応はこれを示しています。

#include<iostream>
class foo {
public:
  int a, b, c;
};

int main() {
    foo f1, f2;
    f1 = f2;
    std::cout << "f1.a and f2.a are: " << f1.a << " and " << f2.a << std::endl;
    f2.a = 0;
    std::cout << "now, f1.a and f2.a are: " << f1.a << " and " << f2.a << std::endl;
}

印刷する:

f1.a and f2.a are: 21861 and 21861
now, f1.a and f2.a are: 21861 and 0
4
fabian

A、b、cがクラスの場合、それらのクラスの代入演算子が呼び出されるため、コンパイラーは未加工のメモリの内容を単にコピーするのではありません。指摘されているため、ぶら下がっているポインタの可能性があります。

1
TimA

番号。 operator=はコピーをまったく実行しません。 代入演算子であり、copy演算子ではありません。

デフォルトの割り当て演算子は、各メンバーを割り当てます。

1
Edward Strange

以下のコードスニペットに示すように、STLの=(代入)演算子はディープコピーを実行します。

#include <iostream>
#include <stack>
#include <map>
#include <vector>

using namespace std;

int main(int argc, const char * argv[]) {
    /* performs deep copy */
    map <int, stack<int> > m;
    stack <int> s1;
    stack <int> s2;

    s1.Push(10);
    cout<<&s1<<" "<<&(s1.top())<<" "<<s1.top()<<endl;   //0x7fff5fbfe478 0x100801200 10

    m.insert(make_pair(0, s1));
    cout<<&m[0]<<" "<<&(m[0].top())<<" "<<m[0].top()<<endl; //0x100104248 0x100803200 10

    m[0].top() = 1;
    cout<<&m[0]<<" "<<&(m[0].top())<<" "<<m[0].top()<<endl; //0x100104248 0x100803200 1

    s2 = m[0];
    cout<<&s2<<" "<<&(s2.top())<<" "<<s2.top()<<endl;   //0x7fff5fbfe448 0x100804200 1

    s2.top() = 5;
    cout<<&s2<<" "<<&(s2.top())<<" "<<s2.top()<<endl;   //0x7fff5fbfe448 0x100804200 5
    cout<<&m[0]<<" "<<&(m[0].top())<<" "<<m[0].top()<<endl; //0x100104248 0x100803200 1

    cout<<endl<<endl;

    map <int, stack<int*> > mp;
    stack <int*> s1p;
    stack <int*> s2p;

    s1p.Push(new int);
    cout<<&s1p<<" "<<&(s1p.top())<<" "<<s1p.top()<<endl;    //0x7fff5fbfe360 0x100805200 0x100104290

    mp.insert(make_pair(0, s1p));
    cout<<&mp[0]<<" "<<&(mp[0].top())<<" "<<mp[0].top()<<endl;  //0x1001042e8 0x100806200 0x100104290

    mp[0].top() = new int;
    cout<<&mp[0]<<" "<<&(mp[0].top())<<" "<<mp[0].top()<<endl;  //0x1001042e8 0x100806200 0x100104320

    s2p = mp[0];
    cout<<&s2p<<" "<<&(s2p.top())<<" "<<s2p.top()<<endl;    //0x7fff5fbfe330 0x100807200 0x100104320

    s2p.top() = new int;
    cout<<&s2p<<" "<<&(s2p.top())<<" "<<s2p.top()<<endl;    //0x7fff5fbfe330 0x100807200 0x100104340
    cout<<&mp[0]<<" "<<&(mp[0].top())<<" "<<mp[0].top()<<endl;  //0x1001042e8 0x100806200 0x100104320

    cout<<endl<<endl;

    vector<int> v1,v2;
    vector<int*> v1p, v2p;

    v1.Push_back(1);
    cout<<&v1<<" "<<&v1[0]<<" "<<v1[0]<<endl;   //0x7fff5fbfe290 0x100104350 1

    v2 = v1;
    cout<<&v2<<" "<<&v2[0]<<" "<<v2[0]<<endl;   //0x7fff5fbfe278 0x100104360 1

    v2[0] = 10;
    cout<<&v2<<" "<<&v2[0]<<" "<<v2[0]<<endl;   //0x7fff5fbfe278 0x100104360 10
    cout<<&v1<<" "<<&v1[0]<<" "<<v1[0]<<endl;   //0x7fff5fbfe290 0x100104350 1

    cout<<endl<<endl;

    v1p.Push_back(new int);
    cout<<&v1p<<" "<<&v1p[0]<<" "<<v1p[0]<<endl;    //0x7fff5fbfe260 0x100104380 0x100104370

    v2p = v1p;
    cout<<&v2p<<" "<<&v2p[0]<<" "<<v2p[0]<<endl;    //0x7fff5fbfe248 0x100104390 0x100104370

    v2p[0] = new int;
    cout<<&v2p<<" "<<&v2p[0]<<" "<<v2p[0]<<endl;    //0x7fff5fbfe248 0x100104390 0x1001043a0
    cout<<&v1p<<" "<<&v1p[0]<<" "<<v1p[0]<<endl;    //0x7fff5fbfe260 0x100104380 0x100104370

    return 0;
}
0
kallumama24