13

次のクラスを検討してください。

class A {

char *p;
int a, b, c, d;

public:
   A(const &A);
};

"p" のディープ コピーを行うには、コピー コンストラクターを定義する必要があることに注意してください。これには 2 つの問題があります。

  1. ほとんどのフィールドは単純にコピーする必要があります。それらを 1 つずつコピーするのは見苦しく、エラーが発生しやすくなります。

  2. さらに重要なことに、新しい属性がクラスに追加されるたびに、コピー コンストラクターを更新する必要があり、メンテナンスの悪夢が生まれます。

私は個人的に次のようなことをしたいと思っています:

A(const A &a) : A(a)
{
   // do deep copy of p
   :::
}

したがって、デフォルトのコピー コンストラクターが最初に呼び出され、次にディープ コピーが実行されます。
残念ながら、これはうまくいかないようです。

これを行うより良い方法はありますか?1 つの制限 - 共有/スマート ポインターを使用できません。


Sbi の提案は非常に理にかなっています。リソースを処理するためのラッパー クラスを作成すると思います。ブースト ライブラリはすべてのプラットフォームで利用できるとは限らないため、shared_ptr を使用したくありません (少なくとも標準ディストリビューションでは利用できません。OpenSolaris はその例です)。

何らかの方法でコンパイラにデフォルトのコンストラクタ/代入演算子を作成させ、その上に機能を追加することができれば素晴らしいと思います。手動で作成されたコピー コンストラクター/代入演算子関数は、作成するのが面倒で、維持するのは悪夢だと思います。したがって、私の個人的な経験則は、カスタム コピー コンストラクター/代入演算子を絶対に避けることです。

皆さんの回答と役立つ情報に感謝し、私の質問のタイプミスについて申し訳ありません. 携帯から入力していました。

4

9 に答える 9

20

経験則として、リソースを手動で管理する必要がある場合は、それぞれを独自のオブジェクトにラップします。

それchar*を適切なコピーコンストラクターを使用して独自のオブジェクトに入れ、コンパイラーにコピーコンストラクターを実行させますAこれは、質問で言及していない割り当てと破棄も扱っていることに注意してください。ただし、それでも処理する必要があります。
標準ライブラリには、そのために選択できるいくつかのタイプがstd::stringありstd::vector<char>ます。

于 2010-07-07T07:44:54.370 に答える
4

に置き換えchar*ますstd::string

于 2010-07-07T07:31:14.567 に答える
3

raw ポインタなどの unmanages リソースを管理するには、常に RAII オブジェクトを使用し、リソースごとに正確に 1 つの RAII オブジェクトを使用します。一般に、生のポインタは避けてください。この場合、 を使用するstd::stringのが最善の解決策です。

何らかの理由でそれが不可能な場合は、簡単にコピーできる部分を基本クラスまたはメンバー オブジェクトに分解します。

于 2010-07-07T07:41:22.517 に答える
3

コピー可能なメンバーを POD 構造体に分離し、マネージド コピーを必要とするメンバーを個別に管理できます。

データ メンバーは非公開であるため、これはクラスのクライアントには見えない可能性があります。

例えば

class A {

char *p;

struct POData {
    int a, b, c, d;
    // other copyable members
} data;

public:
   A(const &A);
};

A(const A& a)
    : data( a.data )
{
    p = DuplicateString( a.p );
    // other managed copies...
    // careful exception safe implementation, etc.
}
于 2010-07-07T07:55:01.190 に答える
2

ここではスマート ポインターを使用する必要があります。

これにより、コピー コンストラクターと影響演算子 ( operator=) の両方を書き換える必要がなくなります。

これらはどちらもエラーが発生しやすいものです。

よくある間違いは、operator=そのように実装することです。

SomeClass& operator=(const SomeClass& b)
{
  delete this->pointer;
  this->pointer = new char(*b.pointer); // What if &b == this or if new throws ?

  return *this;
}

次の場合に失敗します。

SomeClass a;
a = a; // This will crash :)

スマート ポインターは既にこれらのケースを処理しており、明らかにエラーが発生しにくくなっています。

さらに、スマート ポインタなどboost::shared_ptrは、カスタム解放関数を処理することもできます (デフォルトでは を使用しますdelete)。実際には、生のポインターの代わりにスマート ポインターを使用することが非現実的であるという状況に直面することはめったにありませんでした。

簡単なメモ:boostスマート ポインター クラスは、(テンプレートに基づいて) ヘッダーのみで設計されているため、追加の依存関係は必要ありません。(場合によっては重要です)それらを含めるだけで、すべてがうまくいくはずです。

于 2010-07-07T07:44:08.927 に答える
0

クラスにリソースを管理する機能が1つない限り、リソースを直接管理しないでください。常に何らかの説明のスマートポインタまたはカスタム管理クラスを使用してください。通常、可能であれば、暗黙的なコピーコンストラクターを残すのが最善です。このアプローチにより、デストラクタと代入演算子のメンテナンスも簡単になります。

于 2010-07-07T11:52:53.633 に答える
0

問題は、クラスにディープ コピー セマンティクスを備えたポインターが本当に必要かということです。私の経験では、ほとんどの場合、答えはノーです。シナリオを説明していただければ、別の解決策をご案内できるかもしれません。

とはいえ、この記事では、ディープ コピー セマンティクスを使用したスマート ポインターの実装について説明します。

于 2010-07-07T07:50:44.207 に答える
0

したがって、デフォルトのコピー コンストラクターが最初に呼び出され、次にディープ コピーが実行されます。残念ながら、これはうまくいかないようです。

これを行うより良い方法はありますか?1 つの制限 - 共有/スマート ポインターを使用できません。

あなたの質問を正しく理解していれば、初期化関数の使用を検討できます。

class A
{
    int i, j;
    char* p;

    void Copy(int ii, int jj, char* pp); // assign the values to memebers of A
public:
    A(int i, int j, char* p);
    A(const A& a);
};

A::A(int i, int j, char* p)
{
    Copy(i, j, p);
}

A::A(const A& a)
{
    Copy(a.i, a.j, a.p);
}

そうは言っても、余分なリソースとして RAII を使用することを検討する必要があります (人々がそれを推奨し続けるのには理由があります :) )。

RAII を使用できない場合でも、メンバーごとにコピー コンストラクターを作成し、初期化子リストを使用することを好みます (実際には、RAII を使用する場合でもそうする方が好きです)。

A::A(int ii, int lj, char* pp)
    : i(ii)
    , j(jj)
    , p( function_that_creates_deep_copy(pp) )
{
}

A::A(const A& a)
    : i(a.i)
    , j(a.j)
    , p( function_that_creates_deep_copy(a.p) )
{
}

これには「明示性」という利点があり、デバッグが簡単です (ステップインして、初期化ごとに何を行うかを確認できます)。

于 2010-07-07T08:24:52.710 に答える
0

RAIIの独自のクラスでポインターをラップし、コンパイラーにコピーコンストラクター、デストラクタ、および代入演算子を合成させる必要があると他の人が言っていることに同意しますが、問題を回避する方法があります:プライベート静的関数を宣言(および定義)します。さまざまなコンストラクターに必要で共通のものは何でも、そこから呼び出します。

于 2010-07-07T08:01:05.457 に答える