2010-11-24 5 views
1

私は多少C++に新しいので、これは非常に基本的な質問です。オーバーロード=コピーされる値の配列がある場合のC++の演算子

私は、このクラスがあるとします。

// file Graph.h 
class Graph { 
public: 
    Graph(int N); // contructor 
    ~Graph();  // destructor 
    Graph& operator=(Graph other); 
private: 
    int * M; 
    int N; 
}; 

// file Graph.cpp 
Graph :: Graph(int size) { 
    M = new int [size]; 
    N = size; 
} 

Graph :: ~Graph() { 
    delete [] M; 
} 

私は[] 内容に配列Mのをコピーします代入演算子を作成するのではなく、私はコピーした後、それを変更したときに、それを上書きしないようにしたいです(これは、実際のポインタをコピーせず、コンテンツだけをコピーすることによって実現され、私が正しいかどうかはわかりません)。これは私が試したものです:

Graph& Graph::operator=(Graph other) { 
    int i; 
    N = other.N; 
    M = new int [N]; 
    for (i = 0; i < N; i++) 
    M[i] = other.M[i]; 
    return *this; 
} 

これは間違いありませんか?これを行う他の方法はありますか?

編集:私が忘れた重要な質問。なぜ私はGraph& operator=(Graph other);のように宣言しなければならないのですか?Graph operator=(Graph other);私の本(C++:The Complete Reference、2版、Herbert Schildt、355-357ページ)に書かれている内容だけではありませんか?

+2

を助けたホープ「....(私は= int型のために」> - あなたは「i」の関数の先頭で宣言することにより、Cの構文を使用する必要がドントFAQ](http://stackoverflow.com/questions/4172722/)に興味があります。 'int *'を 'std :: vector 'に置き換えれば、苦痛の世界を節約できます。 – fredoverflow

答えて

9

標準的な方法は、自分でメモリを管理しないようにstd::vector<int>を使用することです。しかし、運動のために、あなたが欲しいものを行うには正しい方法は、次のとおりです。

#include <algorithm> 

class Graph 
{ 
public:  
    Graph(size_t n) { data_ = new int[n](); size_ = n; } 

    Graph(Graph const& g) 
    { 
     data_ = new int(g.size_); 
     size_ = g.size_; 
     std::copy(g.data_, g.data_ + g.size_, data_); 
    } 

    ~Graph() { delete[] data_; } 

    void swap(Graph& g) throw() 
    { 
     std::swap(data_, g.data_); 
     std::swap(size_, g.size_); 
    } 

    Graph& operator=(Graph g) { g.swap(*this); return *this; } 

private: 
    int* data_; 
    size_t size_; 
}; 

Googleのコードの理論的根拠のための「コピーおよびスワップイディオム」。あなたのアサイメント演算子はメモリをリークします(元の配列は上書きされますが削除されません)。割り当てに失敗すると、壊れたオブジェクトになります。さらに、x = xは、期待されることをしません。これらの3つの落とし穴は一般的であり、コピー&スワップスタイルで代入演算子を書くことで回避されます。

編集:他の質問のために、参照を返すと、a = b = cのような組み込み型に有効な割り当てを連鎖させることができます。それはあなたが望むものであるかもしれないし、そうでないかもしれない(それは通常である)。

+0

は '' copy and swap idiom "'(これは私には馴染めません)というパラメタを、代入演算子に値渡しで渡した理由です。 – davka

+2

@davka:はい。代入演算子は、その引数を「* this」でコピーしてスワップします。値渡し時にコピーが作成され、標準では、必要でない場合にコンパイラーがこのコピーを回避することができます。 –

+0

+1はC++の方法を示唆しています、 'ベクトル' - 私は実際にその部分を強調します。 –

0

また、STDを使用することができます::

std::copyよりここにコピーするか、memcpy配列としても

1

はおそらく、コピーコンストラクタを宣言し、定義したいと思いますすることができます。

実際には、デフォルトのコピーコンストラクタでは、破棄中にダブルdeleteが発生するため、実際には必須です。

私は、代入演算子(コピーの後にスワップが続く)でコンストラクタを使うのはもっと慣れていると思います。

現在のコードは、古いMdeletedではないため、メモリリークがあります。

+0

'M = new int [N];の前に' delete M'を追加する必要がありますか? –

+0

@ラファエル:あなたはそうかもしれませんが、別の問題があります。割り当てが失敗し(例外をスローすることによって)、この失敗をキャッチすると、割り当て先のオブジェクトは元の内容を失います。したがって、一時変数を使用する必要があります。 –

+0

Chowlettが述べたような自己割り当ての危険性は言うまでもありません。 – lijie

0

graph_a = graph_aと書くことは妥当であることを忘れないでください。あなたのコードは最初にgraph_a.Mのために割り当てられたメモリをリークし、コピー後にMに初期化されていないメモリがあります。

コピーアサインを行う前に、同じオブジェクトをそれ自身にコピーしていないことを確認する必要があります(その場合は、ただ戻ることができます)。

3

operator=&が必要です。*thisを返すとコピーが発生しません。より重要な質問は、なぜあなたは何かを返す必要があるということです。答えはa=b=c構文をサポートすることです。

私はmemcpyを組み込みint型の標準配列(またはポインタ)に使うことをお勧めします。この規格では、コンパイラの作者に可能なかぎり最速のプラットフォーム固有の実装を提供する方法を定義しています。型がオブジェクトである場合

は、あなたが構築したオブジェクトへの参照を返す必要があります(コピーコンストラクタと他の悪い事の多くを呼び出すことはありません)

+0

memcpyの説明のために+1 – Francesco

+1

'std :: copy'はオブジェクトをコピーする最も効率的な方法を使用しません(ダムオブジェクトの場合は' memcpy'でしょう)?私はそれが 'memcpy'を使いこなすよりも、C++でもっと慣れていると思うでしょう。 – lijie

+0

@lijie - わかりませんが、おそらく。 –

0

をmemcpyのは使用しないでください。コピーを返した場合は、別のオブジェクトのコピーを作成して、すでに持っているオブジェクトを破棄します。

1

ほぼすべてが言われているが、いくつかの重要な注意事項があります

  • のconst参照としてコンストラクタと代入演算子をコピーするためにパラメータを渡すことをお勧めされ、すなわちconst Graph& other、重いコピーを避けるために、あなたが"copy and swap" idiom
  • を使用している場合を除いて、である必要があります(ほとんどの場合、ほとんどの場合、)は、ctorと代入演算子の両方を提供するか、非公開にして無効にします。それ以外の場合はデフォルトの漏れを引き起こす。
  • 最後に、std::vectorを使用しないでください。パフォーマンスに大きな悪影響を与えることなく、この問題をすべて解決します。

このページが役に立つかもしれません - シンプルかつ包括的な:それのC++ Operator Overloading Guidelines

+0

私は代入演算子に値渡しするケースがあると思います(代入演算子の本体は* thisとパラメータの単なるスワップです)。 – lijie

+0

@lijie:はい、私はアレクサンドルの答えからそれを学びました。しかし、完全なイディオムを使用して有効なケースにする必要があります。 – davka

+0

したがって、最初の箇条書きポイントは変更する必要がありますか?代入演算子に値でパラメータを渡すにはブランケット "no"のように見えます。 – lijie

1

いくつかは、すでに他の人が言ってきたが、我々はあなたが持っている基本的なレイアウトに固執したい場合は、これを変更する必要があります

  • メイク「グラフの他の」定数 - >「constのグラフの他の」
  • が「他」にチェックを持っている(グッド行うには、あなたが「誤って」「その他」のデータを変更ドントので)それがあなたが割り当てているオブジェクト(Lvalue)と同じオブジェクトでないことを確認してください。ところで(私たちは、メモリリークをしたいドン:))

ああ>(この==他&)

  • はM.のメモリを削除した場合 - if文シンプルにすることによってこれを行うことができます。

    はこの[それはあなたが見つけるかもしれない:)

  • 関連する問題