6

ここで奇妙な問題が発生しました。いくつかの仮想メソッドを持つクラスがあると仮定します。特定の状況下では、このクラスのインスタンスはこれらのメソッドの1つを呼び出す必要があります。ほとんどの場合、そのステージでは問題は発生しませんが、仮想メソッドへのポインタがNULLであるため(VSに示されているように)、仮想メソッドを呼び出せないことが判明し、メモリアクセス違反の例外が発生する場合があります。どうしてそれが起こるのでしょうか?

アプリケーションはかなり大きくて複雑なので、どのような低レベルの手順がこの状況につながるのかはよくわかりません。生のコードを投稿することは役に立ちません。

UPD:わかりました。問題の表現はかなり不明確なので、コードは次のようになります。

void MyClass::FirstMethod() const { /* Do stuff */ }
void MyClass::SecondMethod() const
{
    // This is where exception occurs, 
    // description of this method during runtime in VS looks like 0x000000
    FirstMethod(); 
}

コンストラクタやデストラクタは関与しません。

4

7 に答える 7

6

ヒープの破損が考えられます。オブジェクト内のv-tableポインターは脆弱であり、通常はオブジェクト内の最初のフィールドです。オブジェクトに隣接している他の種類のオブジェクトのバッファオーバーフローは、vテーブルポインタをワイプします。多くの場合、はるかに後で、仮想メソッドの呼び出しは失敗します。

もう1つの典型的なケースは、「this」ポインタが正しくないことです。通常はNULLまたは低い値です。これは、メソッドを呼び出すオブジェクト参照が不正な場合に発生します。メソッドは通常どおり実行されますが、クラスメンバーにアクセスしようとするとすぐに爆発します。繰り返しになりますが、ヒープが破損したり、削除されたポインタを使用したりすると、これが発生します。これをデバッグして頑張ってください。それは決して簡単ではありません。

于 2010-02-11T20:19:22.500 に答える
2

おそらく、それ自体がその関数を持たない基本クラスのコンストラクターから関数を(直接的または間接的に)呼び出しているのでしょう。

おそらくどこかに壊れたキャストがあり(reinterpret_cast多重継承が関係している場合のポインターのように)、間違ったクラスのvtableを見ています。

おそらく(しかしありそうもない)あなたはどういうわけかvtableをゴミ箱に捨てました。

関数へのポインタは、このオブジェクトだけですか、それとも同じタイプの他のすべてのオブジェクトに対してnullですか?前者の場合、vtableポインターが壊れており、間違った場所を探しています。後者の場合、vtable自体が壊れています。

于 2010-02-11T19:58:15.280 に答える
1

これが発生する可能性のあるシナリオの1つは、デストラクタまたはコンストラクタで純粋仮想メソッドを呼び出そうとした場合です。この時点で、メソッドの仮想テーブルポインターが初期化されず、クラッシュが発生する可能性があります。

于 2010-02-11T19:56:40.460 に答える
1

SecondMethodの処理中に「this」ポインタが削除される可能性はありますか?

もう1つの可能性は、SecondMethodが実際には無効なポインターで呼び出されており、ネストされた関数呼び出しまでは(未定義の動作によって)機能し、その後失敗する可能性があります。印刷コードを追加できる場合は、これらのメソッドの実行中のさまざまな時点で、「this」や使用されている他のポインターが0xcdcdcdcdや0xfdfdfdfdのようなものであるかどうかを確認してください。これらの値は、メモリの割り当て/割り当て解除でVSによって使用されます(私は信じています)。これが、デバッグモードでコンパイルされたときに機能する理由である可能性があります。

于 2010-02-11T21:11:37.017 に答える
0

あなたが見ている可能性が最も高いのは、実際の問題の副作用です。ヒープまたはメモリが破損しているか、以前に解放されたオブジェクトまたはnullポインタを参照している可能性があります。

同じ場所で一貫してクラッシュし、nullポインターがどこからロードされているかを把握できる場合は、デバッガーを使用して、そのメモリ位置の「書き込み」にブレークポイントを設定することをお勧めします。ブレークポイントがトリガーされると、おそらく実際に破損の原因となったコードを表示しています。

于 2010-02-12T00:57:34.697 に答える
0

Studioがメソッドアドレスの表示に失敗した場合にのみメモリアクセス違反が発生する場合は、デバッグ情報が欠落していることが原因である可能性があります。おそらく、リリース(非デバッグ)コンパイラ/リンカフラグを使用してコンパイルされたコードをデバッグしています。

プロジェクトのC++プロパティでいくつかのデバッグ情報を有効にして、デバッガーを再構築して再起動してみてください。それが役立つ場合は、スタック、変数などの通常の追跡可能なものがすべて表示されます。

于 2010-02-12T01:04:14.233 に答える
0

このポインタがNULLの場合、破損する可能性はほとんどありません。あなたがメモリをゼロにしているのでない限り、あなたは持っているべきではありません。

デバッグ(最適化されていない)ビルドとリリース(最適化された)ビルドのどちらをデバッグしているのかはわかりませんでした。通常、リリースビルドでは、オプティマイザーはこのポインターが必要ない場合は削除します。したがって、最適化されたビルドをデバッグしている場合、このポインターを0として表示しても、何の意味もありません。何が起こっているのかを伝えるには、分解に頼る必要があります。デバッグビルドで問題を再現できない場合は、リリースビルドで最適化をオフにしてみてください。最適化されたビルドをデバッグする場合、C++ではなくアセンブリをデバッグします。

最適化されていないビルドを既にデバッグしている場合は、破損したイメージのデバッグに多くの時間を費やす前に、クリーンな再構築があることを確認してください。デバッグビルドは通常、インクリメンタルにリンクされており、インクリメンタルリンカーはこのような問題を引き起こすことが知られています。クリーンビルドでデバッグビルドを実行しているのに、何が問題だったのか理解できない場合は、スタックダンプとその他のコードを投稿してください。私たちはあなたがそれを理解するのを手伝うことができると確信しています。

于 2010-02-12T05:07:34.227 に答える