0

次のものがあるとします。

class StringClass
{
public:
    ...
    void someProcessing( );
    ...
    StringClass& operator=(const StringClass& rtSide);
    ...
private:
    char *a;//Dynamic array for characters in the string
    int capacity;//size of dynamic array a
    int length;//Number of characters in a
};

StringClass& StringClass::operator=(const StringClass& rtSide)
{
    capacity = rtSide.capacity;
    length = rtSide.length;
    delete [] a;
    a = new char[capacity];

    for (int i = 0; i < length; i++)
       a[i] = rtSide.a[i];

    return *this;
}

私の質問は、次のようにオブジェクトをそれ自体に割り当てようとすると、なぜこの代入演算子のオーバーロードの実装が問題を引き起こすのかということです。

StringClass s;
s = s;

私が読んでいる教科書(Absolute C ++)には、delete [] a;「ポインタsaは未定義です。代入演算子によってオブジェクトが破損し、このプログラムの実行がおそらく台無しになっている」と書かれています。

なぜオペレーターがsを破損したのですか?削除した直後にsaを再初期化する場合、なぜこれがプログラムでこのような問題を引き起こし、関数を次のように再定義する必要があるのでしょうか。

StringClass& StringClass::operator=(const StringClass& rtSide)
{
    if (this == &rtSide)
    //if the right side is the same as the left side
    {
        return *this;
    }
    else
    {
        capacity = rtSide.capacity;
        length = rtSide.length;
        delete [] a;
        a = new char[capacity];
        for (int i = 0; i < length; i++)
            a[i] = rtSide.a[i];

        return *this;
    }
}
4

5 に答える 5

5

オブジェクトをそれ自体に割り当て、同じ文字列を指す場合は、whatとapointの両方を削除します。次に、それを再割り当てしますが、ループ内で(それ自体で)コピーしようとしていたデータは、で失われています。rt.adelete [] aart.adelete

ループでは、それ自体によって返されたメモリにたまたまジャンクが含まれている場合は、それをコピーするだけですnew

ちなみに、自己代入の「セーフティネット」を使っても、代入演算子が完全に正常ではないことを確認してください(たとえば、例外安全ではありません)。「ビッグスリー」(コピーコンストラクタ、代入演算子、デストラクタ)を定義する「安全な」方法は、「コピーとスワップのイディオム」を使用することです。

于 2012-11-27T01:26:30.900 に答える
3

自己割り当てする場合deleteは、RHS引数を介して新しく割り当てられたスペースに文字列をコピーする前に、LHS引数を介して文字列を解放()します。これは幸福のレシピではありません。これは未定義の動作であり、何かが起こる可能性があります。クラッシュはもっともらしいです。あなたが本当に運が悪ければ、それはうまくいくように見えるかもしれません。

于 2012-11-27T01:26:50.840 に答える
2

rtSide.aあなたが壊れたの中にいるときの価値が何であるかを考えてくださいoperator=

それはthis->a、あなたがたった今壊した値と同じです。所有されていないメモリへのアクセスは未定義の動作であるため、this-> aへのアクセスは未定義の動作です(解放したばかりなので)。

delete [] a;
a = new char[capacity];

for (int i = 0; i < length; i++)
   a[i] = rtSide.a[i]; //Invalid when this->a == rtSide.a 
   //because rtSide.a is no longer owned by your program.

実際にこれを実行したい場合は、削除する前にaのコピーを作成する必要があります。

char* ca;
if (this == &rtSide) {
    ca = copy of rtSide.a or this->a;
} else {
    ca = rtSide.a;
}

//Do your assigning and whatnot

if (this == &rtSide) {
    delete[] ca;
}

明らかに、すべてのインスタンスのメンバーの一時的なコピーを作成する代わりに、何もしない方がはるかに効率的です。それはするのと同じ概念ですint x = 5; int y = x; x = y;

于 2012-11-27T01:26:40.347 に答える
1

これは、最初にポインタdelete [] a;
を削除し、後で削除した場所からコピーしようとしたためです。

for (int i = 0; i < length; i++)
       a[i] = rtSide.a[i]; //rtSide has already been deleted as 'this' and '&rtSide' are same.

コピー元と同じ場所であり、すでに削除されていることを忘れないでください。したがって、エラー!
あなたが投稿した後のコードは、別のケースとして自己割り当てをチェックすることによってこの問題を修正します。

于 2012-11-27T01:26:57.087 に答える
0
delete [] a;
a = new char[capacity];

for (int i = 0; i < length; i++)
   a[i] = rtSide.a[i];

それが理由です。このように考えてください:

ポイントを削除してから、新しいメモリチャンクを割り当てます。メモリの新しいチャンクには、新しいデータになるガベージが含まれています。a[i] = rtSide.a[i];ガベージをそれ自体にコピーするだけのループと混同しないでください。

覚えておいてください、thisそしてrtSide両方ともあなたを同じオブジェクトに導きます。を参照するオブジェクトを使用してオブジェクトを変更すると、this変更rtSideされます。

于 2012-11-27T01:28:51.370 に答える