14

C ++をプログラミングするとき、私たちは必要に応じてコピーコンストラクターを作成していました(またはそう教えられました)。数年前にJavaに切り替えたとき、代わりにCloneableインターフェースが使用されていることに気づきました。C#は、ICloneableインターフェイスを定義する同じルートをたどりました。クローン作成はOOPの定義の一部であるように私には思えます。しかし、なぜこれらのインターフェイスが作成され、コピーコンストラクターが削除されたように見えるのでしょうか。

考えてみると、(基本型への参照のように)型がわからないオブジェクトのコピーを作成する必要がある場合、コピーコンストラクタは役に立たないだろうと思いました。これは論理的なようです。しかし、私が知らない他の理由があるのではないかと思います。その理由として、Cloneableインターフェースがコピーコンストラクターよりも好まれてきました。

4

6 に答える 6

18

これは、JavaやC#で参照型のコピーコンストラクターが本質的に必要ないためだと思います。C ++では、オブジェクトに名前が付けられます。ポインタを返すには動的メモリを割り当てる必要があり、管理に時間がかかり、手間がかかるため、たとえば関数から戻るときに、それらをコピー(およびC ++ 1xで移動)できます(ほとんどの場合移動します)。構文はT(x)であるため、T参照を取得するコンストラクターを作成することは理にかなっています。C ++はクローン関数を作成できませんでした。これは、オブジェクトを値で再度(したがって別のコピーで)返す必要があるためです。

しかし、Javaでは、オブジェクトには名前がありません。それらへの参照のみがあり、コピーできますが、オブジェクト自体はコピーされません。実際にそれらをコピーする必要がある場合は、clone呼び出しを使用できます(ただし、他の回答で読み取ったクローンには欠陥があります。私はJavaプログラマーではないため、コメントできません)。オブジェクト自体ではなく、オブジェクトへの参照が返されるため、クローン関数で十分です。また、クローン関数をオーバーライドすることもできます。これは、コピーコンストラクターでは機能しません。ちなみに、C ++では、ポリモーフィックオブジェクトをコピーする必要がある場合、clone関数も必要です。いわゆる仮想コピーコンストラクタという名前が付けられています。

于 2008-12-23T06:49:53.737 に答える
4

C ++とJava(およびC#)は同じものではないからです。インターフェイスは言語の一部ではないため、C++には組み込みのインターフェイスがありません。抽象クラスでそれらを偽造することはできますが、C++についての考え方ではありません。また、C ++では、割り当ては通常深いです。

JavaおよびC#の割り当てでは、ハンドルを内部オブジェクトにコピーするだけです。基本的にあなたが見るとき:

SomeClass x = new SomeClass();

JavaまたはC#には、C++には存在しないレベルの間接参照が組み込まれています。C ++では、次のように記述します。

SomeClass* x = new SomeClass();

C ++での割り当てには、逆参照された値が含まれます。

*x = *another_x;

Javaでは、* xのような間接参照演算子がないため、「実際の」オブジェクトにアクセスできます。したがって、ディープコピーを実行するには、clone()という関数が必要です。そして、JavaとC#の両方がその関数をインターフェースにラップしました。

于 2008-12-23T06:42:53.343 に答える
3

これは、最終型の問題であり、コピーコンストラクターによって対処されないスーパークラスを介してクローン操作をカスケードする問題です。これらは拡張可能ではありません。しかし、Javaクローンメカニズムもひどく壊れていると広く考えられています。特に、サブクラスがclone()を実装していないが、cloneableを実装しているスーパークラスから継承している場合の問題。

どのパスを選択する場合でも、クローン作成を慎重に調査することを強くお勧めします。clone()オプションを選択する可能性がありますが、正しく実行する方法を正確に理解していることを確認してください。これは、equals()やhashCode()に似ています。表面的には単純に見えますが、正確に実行する必要があります。

于 2008-12-23T06:40:59.247 に答える
2

あなたは適切なポイントを得ていないと思います。私はあなたに私の2セントを差し上げます。

根本的に問題があります: 正確なクラス タイプを知らずにクラスのクローンを作成することです。コピーコンストラクターを使用する場合はできません。

次に例を示します。

class A {
public A(A c) { aMember = c.aMember }
    int aMember;
}

class B : A {
public B(B c) : base(c) { bMember = c.bMember }
    int bMember;
}

class GenericContainer {
public GenericContainer(GenericContainer c) {
    // XXX Wrong code: if aBaseClass is an instance of B, the cloned member won't 
    // be a B instance!
    aBaseClass = new A(c.aBaseClass);
}
    A aBaseClass;
}

Clone メソッドは、virtual を宣言すると、ジェネリック メンバーの適切なクラス インスタンスを作成できます。

この問題は、すべての言語、C# C++ または Java に共通しています...

多分これはあなたが意味していたものですが、私はこれをどの回答からも理解できません。

于 2009-12-19T11:09:16.907 に答える
1

これをJavaに追加したかったのですが、コピーコンストラクターは完全に役に立たないわけではありません。

クラスに、たとえば、可変の非最終型のプライベートインスタンス変数があり、変数のDateセッターとゲッターがある場合があります。セッターでは、指定された日付のコピーを作成する必要があります。これは、呼び出し元が後で日付を変更して、オブジェクトの内部状態を操作する可能性があるためです(通常は偶然ですが、意図的な場合もあります)。ゲッターでも同様の注意が必要です。

防御コピーは呼び出すことで実装できますがclone()(クラスDateはクローン可能です)、悪意のある呼び出し元は、メソッドDateをオーバーライドするサブクラスを使用してセッターを呼び出す可能性があるため、呼び出し元はオブジェクトを操作できる可能性があります。ここで、コピーコンストラクターが機能します。を呼び出すことにより、2つの日付インスタンス間の接続なしで、指定された日付と同じタイムスタンプを持つ新しいインスタンスを確実に取得できます。ゲッターでは、プライベート変数がクラスになることがわかっているため、cloneメソッドを使用できますが、一貫性を保つために、通常はコピーコンストラクターも使用されます。clone(){return this;}new Date(theDate)DateDate

Dateまた、クラスがfinal(呼び出しclone()が安全)または不変(コピーは不要)の場合は、コピーコンストラクターが必要になることに注意してください。

于 2011-04-02T17:40:03.230 に答える
0

コピーコンストラクターを定義すると、参照自体を二度と渡すことができなくなったからだと思います。(それを実行する関数がない限り...しかし、clone()メソッドを使用するよりも簡単ではありません。)C ++では、問題はありません。オブジェクト全体またはその参照を渡すことができます。

于 2010-01-11T04:14:52.660 に答える