2

これは、実際の問題に基づいて作成されたシナリオです。基本クラスがある場合:

class Vehicle
{
public:
     void run() { incrementStuff(); }
     virtual void incrementStuff() { } 
};

そして派生クラス:

class Car : public Vehicle
{
public:
     Car() : Vehicle() { stuff = new StuffClass(); }
     ~Car()  { delete stuff; stuff = NULL; }

     virtual void incrementStuff() { if (stuff != NULL) stuff->increment(); } 

protected:
     StuffClass* stuff;
};

次に、Vehicle :: run()を定期的に呼び出すスレッドがあるとします。最終的に車へのポインタを削除する別のスレッドがあります。破壊の順序により、最初に車が削除され、次に車両が削除されます。

スレッド(Vehicleオブジェクトに存在する)がincrementStuff関数を呼び出した場合、デストラクタが車内で実行された後(ただし、明らかに車両が破壊される前)はどうなりますか?Car :: incrementStuffが実行され、すでに削除されたポインターを逆参照しようとしますか?または、安全で何もしないVehicle :: incrementStuffが呼び出されますか?

Car ::〜Car()の実行中は、スレッドがCar :: incrementStuffを呼び出すことができないと想定します(これはミューテックスによって防止されます)。

このコードはこれを回避するためにリファクタリングされていますが、これがどのように機能するかを誰かが明らかにできるのか、それとも単なる未定義の動作なのか、疑問に思っていました。

より一般的には、Carが破壊された後、Vehicleが破壊される前に、Car :: incrementStuffを呼び出そうとするとどうなりますか?NULLチェックは機能しますか、それともそのメモリはすでに割り当てが解除されており、現在は何でも使用できますか?または、ベースオブジェクトが破棄されるまで、そのメモリは「解放」されませんか?

4

3 に答える 3

6

オブジェクトを削除したCar場合、その後の呼び出しincrementStuff()は未定義の動作です。

余談ですが、おそらくVehicleクラスでデストラクタを作成する必要がありますvirtual。参照:仮想デストラクタを使用する場合

于 2013-01-15T22:31:51.467 に答える
4

難しく考えすぎだよ。既にdeleteed されているポインターを逆参照すると、未定義の動作が呼び出されます。本当に知っておくべきことはそれだけです。すぐに電話しdelete ptr; ptrても無効です。

このオブジェクトへのアクセスを同期します。

于 2013-01-15T22:26:14.423 に答える
2

あるスレッドが値を読み取り、別のスレッドが値を書き込んでいます。これはデータ競合です。2 つのアクションが適切に同期されていない場合、動作は未定義です。

于 2013-01-15T22:36:35.380 に答える