4

私は現在、古い大学の教科書を読み直しながら演算子のオーバーロードについて調査していますが、何かを誤解していると思うので、これが一部の回答者にとって簡単な評判になることを願っています. これが重複している場合は、正しい方向に向けてください。

(この段階では) 1 つのメンバー val (int) を持つ単純なカウンター クラスを作成しました。

これらのカウンターのうち 3 つを varOne から varThree に初期化し、3 番目のカウンターを最初の 2 つの合計にします (たとえば、以下のコードでは varThree.val が 5 に設定されています)。

counter::counter(int initialVal)
{
    val = initialVal;
    //pVal = new int;
    //*pVal = 10; // an arbitrary number for now
}

int main (int argc, char const* argv[])
{
    counter varOne(3), varTwo(2), varThree;
    varThree = varOne + varTwo;

    return 0;
}

次のように operator+ をオーバーロードしました。

counter operator+(counter& lhs, counter& rhs)
{
    counter temp(lhs.val + rhs.val);
    return temp;
}

これを非メンバー関数にし、プライベート値にアクセスできるようにカウンター クラスのフレンドにしました。

私の問題は、別のプライベート メンバーpVal (int へのポインター) を追加するときに始まります。varThree = varOneこれを追加すると、varOne が破棄されても varThree.pVal は引き続き同じメモリ ビットを指しているため、単純なコピーを実行できなくなります。

私はoperator=次のようにオーバーロードしました。

int counter::getN()
{
    return *newVal;
}

counter& counter::operator=(counter &rhs)
{
    if (this == &rhs) return *this;
    val = rhs.val;
    delete pVal;
    pVal = new int;
    *pVal = rhs.getN();
    return *this;
}

varThree = varOne今、すべてが正しくコピーされるようなことをするvarThree = varOne + varTwoと、次のエラーが発生します。

counter.cpp: In function ‘int main(int, const char**)’:
counter.cpp:96: error: no match for ‘operator=’ in ‘varThree = operator+(counter&, counter&)(((counter&)(& varTwo)))’
counter.cpp:55: note: candidates are: counter& counter::operator=(counter&)
make: *** [counter] Error 1

counter::operator=からの戻り出力に対処するのに問題があるように見え、返される型を受け入れるためにさらにoperator+オーバーロードする必要がありますが、運がなく、何かをしたのではないかと考え始めています根本的に間違っています。operator=operator+

4

3 に答える 3

11

パラメータを const 参照として渡す必要があります。例えば:

counter& counter::operator=( const counter &rhs )

operator+() についても同様です。これは、一時的な値を関数パラメーターにバインドできるようにするために必要です。値で戻ると一時的な値が作成されるため、次のように言うと:

varOne + varTwo

名前のない一時が作成されます。これは正しいことですが、代入 op などの関数がパラメーターを const にすることで、そのような値を受け入れることができるようにする必要があります。

クラスのコピー コンストラクタとデストラクタも実装する必要がありますが、これらがなくても (残念ながら) コンパイル エラーは発生しません。

于 2009-06-07T14:13:07.253 に答える
1

この問題に対処する別の方法は、PImpl パターンを使用して代入演算子を交換することです。まだ counter(int) コンストラクターがあると仮定すると、operator= を次のように記述できます。

counter& counter::operator=(const counter& rhs) {
  counter temp(rhs.getN());
  std::swap(&pVal,rhs.pVal);
  return *this;
}

これには、コンストラクタとデストラクタ内の乱雑なメモリ管理関数を本来あるべき場所に残すという利点があります。

于 2009-06-07T15:03:24.967 に答える
1

ここでの重要な点は (以前のポスターで触れたように)、強調する価値があるのは、C++ の式は右辺値または左辺値のいずれかに分類できるということです。

それらはこれらのカテゴリの背後にある多くの詳細ですが、直感を導く便利なヒューリスティックは次のとおりです。式 (変数など) のアドレスを取得できる場合、それは左辺値です (ここでの話にはさらに多くのことがありますが、これは開始するのに適した場所です)。

それが本当に左辺値ではない場合、それは右辺値です。右辺値の便利なヒューリスティックは、コードを機能させるためにコンパイラがインスタンス化する「隠された」一時オブジェクトを表すことです。これらのオブジェクトは、舞台裏でコンパイラによって作成および破棄されます。

なぜこれがここに関係するのですか?

さて、C++98/03 (あなたが使用していると思われるもの) では、次の 2 つの規則を覚えておいてください。

1) 左辺値式のみが非 const 参照にバインドできます (キャストを無視します) 2) 右辺値式は const 参照にのみバインドできます (キャストを無視します)

ここで例が役立ちます:

// Consider the function foo below - it returns an int - 
// whenever this function is called, the compiler has
// to behave as if a temporary int object with the value 5 is returned.
// The use of 'foo()' is an expression that is an rvalue - try typing &foo() -
// [Note: if foo was declared as int& foo(), the story gets complicated, so
//   i'll leave that for another post if someone asks]

int foo() { return 5; }

void bind_r(int& r) { return; }
void bind_cr(const int& cr) { return; }

int main()
{
   int i = 10;  // ok
   int& ri = i; // ok binding lvalue to non-const reference, see rule #1
   int& ri2 = foo(); // Not ok, binding a temporary (rvalue) to a non-const reference 
        // The temporary int is created & destroyed by compiler here

   const int& cri = foo(); // ok - see rule #2, temporary int is NOT destroyed here

  //Similarly
   bind_r(i); // ok - rule #1
   bind_r(foo()); // NOT ok - rule #2
   bind_cr(foo()); // ok - rule #2


  // Since the rules above keep you out of trouble, but do not exhaust all possibilities
  // know that the following is well-formed too:
  const int& cri2 = i;
  bind_cr(i);
  bind_cr(cri);
  bind_cr(cri2); 

}

右辺値を const 参照にバインドすると、基本的に一時オブジェクトの有効期間が参照の有効期間 (この場合はスコープ) まで延長されます (コンパイラはその式の最後でそれを破棄することはできません) -したがって、有効なオブジェクトへの参照になります。

他のポスターの1つが正しく推奨しているように、これが、代入演算子を非const参照だけでなくconst参照を受け入れると宣言する必要がある理由を理解するのに役立つことを願っています.

ps コードには他にもいくつかの問題があります (オブジェクトが割り当てごとに排他的に指すメモリを破棄および作成する理由、およびコピー コンストラクターとデストラクターの欠如など)。この投稿が表示されたら、私はそうします:)

ps また、C++0x は右辺値に優先的にバインドし、プログラマーに非常に強力な最適化の機会を提供する右辺値参照 (非定数) として知られるものを追加することも知っておく価値があります (コンパイラの最適化機能に依存する必要はありません)。 ) - C++ で完全な転送関数を作成するという問題の解決にも役立ちますが、ここでは脱線します ;)

于 2009-06-07T16:30:26.637 に答える