2011-06-29 9 views
3

可能性の重複:
Undefined Behavior and Sequence PointsのPostfix演算子のオーバーロード注文

後置演算子をオーバーロードするとき、私はアクションの順序を理解するトラブルを抱えています。以下の2つの小さな例を調べてみましょう:

int i = 0; 
std::cout << std::endl << "i: " << i; 
i = ++i; 
std::cout << std::endl << "i: " << i; 
i = i++; 
std::cout << std::endl << "i: " << i; 

MyClass myObject; 
std::cout << std::endl << "myObject: " << myObject.getMyValue(); 
myObject = ++myObject; 
std::cout << std::endl << "myObject: " << myObject.getMyValue(); 
myObject = myObject++; 
std::cout << std::endl << "myObject: " << myObject.getMyValue(); 

2つの非常に異なる動作が出現します。出力は次のようになります。

i: 0 
i: 1 
i: 2 
myObject: 0 
myObject: 1 
myObject: 1 

異なる動作があります。私のオーバーロードされた演算子メソッドの概要を次に示します。

MyClass & MyClass::operator++() 
{ 
    ++myValue; 
    return *this; 
} 

MyClass MyClass::operator++ (int postfixFlag) 
{ 
    MyClass myTemp(*this); 
    ++myValue; 
    return myTemp; 
} 

いいえ。プレフィックスは意味があります。あなたは必要なものを増やして、同じオブジェクトを返します。割り当てられた場合は、変更されました。しかし、ポストフィックスは私を踏み外すものです。割り当てをしてからインクリメントするはずです。ここで私たちは自分自身を割り当てています。したがって、組み込みの整数型では意味があります。私はiの値をそれ自身に割り当て、次にiが増分されます。けっこうだ。しかし、MyClassがintのレクリエーションであるとしましょう。 0から始まり、接頭辞をインクリメントして1になります。次に、キー行。 myObject = myObject++。それはmyObject = myObject.operator++(int postfixFlag)と同じことです。それは呼び出される。 myTempは値1で初期化されます.2にインクリメントされます。これは、別のオブジェクトに割り当てている場合に機能します。しかしここで私は自分自身を割り当てているので、2にインクリメントした後、myObjectは、初期値で初期化された返されたtempオブジェクトと等しく設定され、1に戻ります!それは理にかなっている。しかし、それは根本的に異なる行動です。

どうすれば回避できますか? intはどうしますか?この方法は一般にどのように書かれていますか?これに関するC++の動作とデザインに関するコメントはありますか?書籍やオンラインの例は、常に上の方法でバリアントを使用するように見えるので、私はちょっと困っています。

読んでいただきありがとうございました。ご了承ください。

+6

'私は=私++が'と私= ++ i'両方定義されていない動作を示します。 –

+2

あなたの 'int'の振る舞いは未定義です...異なるコンパイラ、バージョン、最適化レベル、ターゲットCPUで異なる結果を得ることができます。まったく明らかな理由もありません。*シーケンスポイントをバックグラウンドで読む必要があります* ...ここにはたくさんの質問がありますそれらに対処する。 –

+1

[C++ FAQ](http://stackoverflow.com/questions/4176328/undefined-behavior-and-sequence-points)もあります。 – Xeo

答えて

5

他の人が言ったように、intでは動作は定義されていません。コピーを作る

  1. :しかし、私はあなたのMyClassのために、それは今までに2

    トリックにあなたはPostfixバージョンでは以下の3つのステップを取っていることをされてきていない理由を説明しようと思いましたthismyTempmyValue == 1)と呼ばれます。

  2. this->myValue(だからmyTemp.myValue == 1; this->myValue == 2)です。
  3. 返信myTempmyValue == 1)。

だからthisを変更しているが、myObject++を呼び出すコードは再びthisを見に行くことはありません。返される値は、古いmyObjectのコピーのみです。

演算子++のコードは正常です。問題はどのように使用しているかです。プリインクリメントまたはポストインクリメントの結果を同じ変数に書き戻すべきではありません(ビヘイビアは未定義です)。ここでは、より有益であるかもしれないいくつかのコードは次のとおりです。

int i = 0; 
std::cout << "i: " << i << std::endl; 
int j = ++i; 
std::cout << "i: " << i << ", j: " << j << std::endl; 
int k = i++; 
std::cout << "i: " << i << ", k: " << k << std::endl; 

MyClass myObject; 
std::cout << "myObject: " << myObject.getMyValue() << std::endl; 
MyClass myObject1 = ++myObject; 
std::cout << "myObject: " << myObject.getMyValue() 
    << ", myObject1: " << myObject1.getMyValue() << std::endl; 
MyClass myObject2 = myObject++; 
std::cout << "myObject: " << myObject.getMyValue() 
    << ", myObject2: " << myObject2.getMyValue() << std::endl; 

これは、出力します。

i: 0 
i: 1, j: 1 
i: 2, k: 1 
myObject: 0 
myObject: 1, myObject1: 1 
myObject: 2, myObject2: 1 

を私はむしろ自分自身に戻って割り当てるよりも、そのようにコードを変更し、それが新鮮な変数にそれぞれの時間を割り当てます。 intMyClassの場合の両方で、メイン変数(i/myObject)が両方ともインクリメントされることに注意してください。しかし、プリインクリメントの場合、新変数(j/myObject1)は新しい値をとり、ポストインクリメントの場合は新しい変数(k/myObject2)は古い値をとります。

を編集します。「どのようにintを実行するのですか?」という質問の別の部分に答えるだけです。私は、この質問は、 "プリインクリメントとポストインクリメントのコードは、intクラスのように見えますが、私はどのように私のものを作ることができるのですか?答えは、 "intクラス"がありません。 intはC++の特殊な組み込み型であり、コンパイラはこれを特別に扱います。これらの型は、通常のC++コードでは定義されていません。コンパイラにハードコードされています。

:質問が含まれていなかったことをMyClassためのコードがあり、ここで、自分自身を、これを試してみたい人のために:

class MyClass 
{ 
private: 
    int myValue; 
public: 
    MyClass() : myValue(0) {} 
    int getMyValue() { return myValue; } 
    MyClass& operator++(); 
    MyClass operator++(int postfixFlag); 
}; 
+0

+1私は何とか 'myObject'を使って大文字小文字を無視したので、未定義の動作に飛びついただけです。 :) – Xeo

+0

残念ながら最善の答えを私は外に出てしまった。詳細を気に入ってください – Walker

関連する問題