1. ホーム
  2. c++

C++のメンバ関数で「if(!this)」はどれくらいまずいのか?

2023-10-25 23:21:07

質問

もし、古いコードで if (!this) return; を使用する古いコードを見つけた場合、どの程度深刻なリスクとなるでしょうか。それとも、静かにそのままにしておくことができるコードの臭いのようなものでしょうか。

私は 書く コードを書くつもりはありません。むしろ、私たちのアプリの多くの部分で使用されている古いコア ライブラリで、最近何かを発見しました。

を想像してみてください。 CLookupThingy クラスが 非仮想的な CThingy *CLookupThingy::Lookup( name ) メンバ関数があります。どうやら、当時のプログラマーの一人は、NULL メンバ関数で多くのクラッシュを経験したようです。 CLookupThingy * が関数から渡される多くのクラッシュに遭遇し、何百もの呼び出しサイトを修正するよりも、彼は静かに Lookup() を修正しました。

CThingy *CLookupThingy::Lookup( name ) 
{
   if (!this)
   {
      return NULL;
   }
   // else do the lookup code...
}

// now the above can be used like
CLookupThingy *GetLookup() 
{
  if (notReady()) return NULL;
  // else etc...
}

CThingy *pFoo = GetLookup()->Lookup( "foo" ); // will set pFoo to NULL without crashing

今週初めにこのgemを発見したのですが、今、修正すべきかどうかで葛藤しています。これは、私たちのすべてのアプリで使用されるコア ライブラリにあります。これらのアプリのいくつかは、すでに何百万人もの顧客に出荷されており、そのコードからクラッシュやその他のバグが発生することはなく、正常に動作しているようです。このコードからクラッシュやその他のバグが発生することはありません。 if !this を削除することは、NULL を渡す可能性のある何千もの呼び出しサイトを修正することを意味します。必然的にいくつかは見落とされ、次の の開発期間中にランダムに発生します。

だから、どうしても必要なとき以外は、放っておきたい気持ちもあるんです。

技術的に未定義の動作であることを考えると、どの程度危険なのかというと if (!this) は実際どの程度危険なのでしょうか?修正するために何週間もの労力を費やす価値があるでしょうか、それとも MSVC と GCC は安全に戻ることを期待できるでしょうか?

私たちのアプリケーションは MSVC と GCC でコンパイルされ、Windows、Ubuntu、および MacOS で実行されます。他のプラットフォームへの移植性は関係ありません。問題の関数は、決して仮想でないことが保証されています。

編集してください。 私が求めている客観的な回答は、次のようなものです。

  • 現在のバージョンの MSVC と GCC は、非仮想メンバが暗黙の 'this' パラメータを持つ静的関数であるような ABI を使用しているので、'this' が NULL であっても安全に関数に分岐します。
  • 次のバージョンの GCC では、非仮想関数でさえクラスポインタから分岐ターゲットをロードする必要があるように ABI が変更される予定です。
  • 現在の GCC 4.5 は、非仮想的なメンバーを暗黙のパラメーターによる直接分岐としてコンパイルすることもあれば、クラス オフセット関数ポインターとしてコンパイルすることもあるという矛盾した ABI を備えています。

前者は、コードが臭いけれども壊れそうにないことを意味し、後者はコンパイラーのアップグレード後にテストするもので、後者は高いコストをかけてもすぐに対処する必要があります。

明らかに、これは起こるのを待っている潜在的なバグですが、今は、私たちの特定のコンパイラーでのリスクを軽減することにのみ関心があります。

どのように解決するのですか?

私なら、そのままにしておきます。これは、古風なバージョンとして意図的に選んだのかもしれません。 SafeNavigationOperator . おっしゃるとおり、新しいコードではお勧めしませんが、既存のコードではそのままにしておきます。もし修正するのであれば、そのコードへのすべての呼び出しがテストによって十分にカバーされていることを確認する必要があります。

編集から追加: あなたは を通して、デバッグバージョンのコードでのみ削除することを選択できます。

CThingy *CLookupThingy::Lookup( name ) 
{
#if !defined(DEBUG)
   if (!this)
   {
      return NULL;
   }
#endif
   // else do the lookup code...
}

このように、デバッグモードでテストする機会を与えつつ、本番コード上では何も壊さないでしょう。