16

重複の可能性:
いつコピー コンストラクターを使用する必要がありますか?

C++ コピー コンストラクターがなぜそれほど重要なのでしょうか? 私はそれらについて学んだばかりで、何が彼らについて大騒ぎしているのかよくわかりません. ポインターを使用する場合は、常にクラスのコピー コンストラクターを作成する必要があるようですが、なぜでしょうか?

ありがとう、ボダ・シド。

4

5 に答える 5

30

言語には「コピー セマンティクス」があるため、コピー コンストラクターと代入演算子は C++ で非常に重要です。つまり、パラメーターを渡すかコンテナーに値を格納すると、オブジェクトのコピーが渡されるか格納されます。C++ はどのようにしてオブジェクトのコピーを作成したり代入を実行したりできますか? ネイティブ型の場合はそれ自体で認識しますが、ユーザー定義型の場合は代わりに、メンバーごとのコピーの構築または割り当てを自動的に生成します。

たとえば、次のように宣言すると、より明確になります。

class P2d
{
    public:
        double x, y;
        P2d(double x, double y) : x(x), y(y)
        { }
};

C++ コンパイラは、コードを自動的に完成させます。

class P2d
{
    public:
        double x, y;
        P2d(double x, double y) : x(x), y(y)
        { }

        P2d(const P2d& other) : x(other.x), y(other.y)
        { }

        P2d& operator=(const P2d& other)
        {
            x = other.x;
            y = other.y;
            return *this;
        }
};

これらの自動生成されたコピー コンストラクターと代入演算子は、クラスに適していますか? 多くの場合、そうです...しかし、もちろん、それらの実装は完全に間違っているかもしれません。たとえば、オブジェクト内にポインターが含まれている場合、オブジェクトのコピーを作成したいときにポインターをコピーするだけでは適切ではありません。

C++ がオブジェクトの多くのコピーを行うこと、および定義したクラスに対して C++ が行うコピーの種類を理解する必要があります。自動的に生成されたコピーが必要なものでない場合は、独自の実装を提供するか、クラスのコピーを禁止する必要があることをコンパイラに伝える必要があります。

プライベートコピー コンストラクターと代入演算子を宣言し、実装を提供しないことで、コンパイラがコピーを作成しないようにすることができます。これらはプライベート関数であり、それらを使用する外部コードはコンパイラ エラーを受け取るため、それらを宣言したが実装していないため、誤ってクラス内でコピーを作成してしまうとリンク エラーが発生します。実装。

例えば:

class Window
{
    public:
        WindowType t;
        Window *parent,
               *prev_in_parent, *next_in_parent,
               *first_children, *last_children;
        Window(Window *parent, WindowType t);
        ~Window();

    private:
        // TABOO! - declared but not implemented
        Window(const Window&); // = delete in C++11
        Window& operator=(const Window&); // = delete in C++11
};

最後の部分がばかげていると思われる場合 (実装で誤ってコピーを作成する方法)、C++ では言語が物事をコピーするという概念に基づいて構築されているため、誤って余分なコピーを作成するのは非常に簡単であることに注意してください。

ゴールデン ルールは、クラスにデストラクタがある場合 (クリーンアップを行う必要があるため)、メンバーごとのコピーは適切ではない可能性が高いということです...また、特別なロジックを実行する場合コピー構築の場合、代入でも同様のロジックが必要になる可能性があります (逆も同様です)。そのため、 Big Threeとして知られるルールは、クラスにカスタム デストラクタ、コピー コンストラクタ、代入演算子がないか、またはクラスにこれら 3 つすべてが必要であると述べています。

このルールは非常に重要です。たとえば、特別なケースでデストラクタが必要なだけのクラスになった場合 (賢明なケースは考えられません...しかし、見つけたとしましょう)、次のように追加することを忘れないでください。あなたがそれについて考え、暗黙的に生成されたコピーコンストラクターと代入演算子が問題ないことを知っているというコメント。他の 2 つについてメモを追加しないと、コードを読んだ人は誰でも、単にそれらのことを忘れていると思うでしょう。

アップデート

C++ は進化しており、ここで述べられていることのほとんどは今でも有効ですが、この言語は、コピーと割り当てが許可されていないことをコンパイラに通知するためのより優れた方法を提供します。

新しい構文 (C++11 以降で有効) は次のとおりです。

struct Window {
    ...
    Window(const Window&) = delete;
    Window& operator=(const Window&) = delete;
};
于 2010-07-31T15:17:38.197 に答える
7

単純: C++ がデフォルトのコピー コンストラクターを使用する場合、ポインターはコピーされますが、ポイントされているデータはコピーされません。結果: 同じデータを指す 2 つのオブジェクト。両方がそのデータを所有していると考え、デストラクタが呼び出されたときにポインタを削除すると、問題が山積みになります...

于 2010-07-31T14:45:03.200 に答える
7

C++ コピー コンストラクターがなぜそれほど重要なのでしょうか?

コピー コンストラクターは、次のいずれかの理由で、他のほとんどの言語では必要ありません。

  • オブジェクトのコピーは常に安全です。
  • ポインター/参照 (Java、Python など) しかありません。この場合、コピーはまれであり、またはメソッドcopy()clone()実行できます。

C++ は値のセマンティクスを優先しますが、多くのポインターも使用します。つまり、次のことを意味します。

  • オブジェクトは頻繁にコピーされ、
  • 他の人が言及した理由により、浅いコピーと深いコピーのどちらが必要かを指定する必要があります。
于 2010-07-31T15:29:11.140 に答える
4

C ++のすべてのクラスには、オブジェクトの浅いコピーを実行する暗黙のコピーコンストラクターがあります。浅いとは、メンバーの価値をコピーすることを意味します。したがって、ポインタがある場合は、ポインタ値がコピーされるため、両方のオブジェクトが同じものを指します。

ほとんどの場合、これは望ましくないため、独自のコピーコンストラクターを定義する必要があります。

多くの場合、オブジェクトのコピーを作成することすらしたくないので、コピーコンストラクターを宣言するのは良いスタイルですが、それを定義することはしません。(ヘッダーファイルの空の宣言)。

次に、誤ってコピーを作成した場合(たとえば、オブジェクトを返す、参照によるパラメーターメソッドを宣言するときに&を忘れたなど)、リンカーエラーが発生します。

コピーコンストラクターをprivateと宣言すると、コンパイラーエラーも発生します(クラス外で使用した場合)。

要約すると、コピーコンストラクターを明示的に宣言するのは常に良いスタイルです-特に必要がない場合は、次のように記述してください。

private:
  MyClass(const MyClass&);

クラス定義で、クラスのコピーコンストラクターを無効にします。

于 2010-07-31T15:13:25.053 に答える
1

ディープコピーとシャローコピー

于 2010-07-31T14:54:36.480 に答える