これは、実際の問題に基づいて作成されたシナリオです。基本クラスがある場合:
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チェックは機能しますか、それともそのメモリはすでに割り当てが解除されており、現在は何でも使用できますか?または、ベースオブジェクトが破棄されるまで、そのメモリは「解放」されませんか?