web-dev-qa-db-ja.com

生のポインターの使用を避けることは、プロのプログラマーの間で一般的な習慣ですか?

C++ツールとライブラリは、これまで以上に強力になりました。

たとえば、配列をベクトルに置き換えることができます。ポインタを参照で置き換えることができます。スマートポインターを使用できます。

生のポインターの使用を避けることは、プロのプログラマーの間で一般的な習慣ですか?

7
user36522

(デバイスドライバーではなくエンタープライズFooの作成について話しているように)十分に高いレベルであり、外部依存関係がない新しいグリーンフィールドプロジェクトを開始する場合は、ポインターの使用を完全に回避できます。しかし、ほとんどの開発はまったく新しい開発ではなく、間違いなくポインタを含むC++コードの何百万もの既存の行を活用しています。また、新しいアプリケーションでさえ、ほとんどの場合、ポインタを頻繁に使用する外部ライブラリや他のAPIに依存しています。したがって、ポインタなしで完全に構築できるアプリケーションのセットはかなり小さいです。

ポインタを使用するために必要なコードベースの量を最小限に抑えることができる場合があります。ポインタに依存しないさまざまなライブラリを見つけることができる場合があります(ただし、これは通常、パフォーマンスまたは機能性を犠牲にします)。ポインタに依存する既存のコードとライブラリの上に独自のインターフェイスを作成し、新しいコードベースの大部分でその新しいインターフェイスを使用できます。ただし、新しいレイヤーを追加し始めると、ほとんどの場合、パフォーマンスや柔軟性が犠牲になります。これらのトレードオフが許容できるかどうかは、アプリケーションに大きく依存します。

11
Justin Cave

人間として、私たちは間違いを犯しやすく、間違いを犯しやすいです。したがって、できるだけ安全にプログラムを作成し、コンパイラやValgrindなどのツールを使用して、本番環境でクラッシュする前に問題を発見できるようにする必要があります。

すべてのレベルでポインタを回避することは不可能であることは明らかです–それらは複雑なアーキテクチャにとって非常に貴重な間接化のメカニズムを提供します–「コンピュータサイエンスのすべての問題は、もちろん、多くの間接参照」(ウィーラーの法則)。ただし、生のポインターは、私たちまたはコンパイラーが効率的に推論するには強力すぎる。

  • ポインターがNULLの可能性があります。
  • ポインターは、無効なメモリー、解放されたメモリー、または未割り当てのメモリーを参照する場合があります。こんにちは、segfault!
  • ポインターは、単一の値を指す場合と、配列のように値のシーケンスを指す場合があります。後者の場合、配列のようなポインターに関連付けられた明確な長さが存在しない可能性があり、バッファーのオーバーランを助長します。
  • コンパイラーがポインターを最適化することは困難です。
  • 指摘された値の存続期間を管理することは困難です。値を削除するのは誰の責任ですか?そのメモリを参照する他のポインタで何が起こりますか?

C++は、プログラマーがポインターのようなものを使用している理由をより正確に示すことができるいくつかのメカニズムを提供します。

  • 参照はnull以外であることが保証されていますが、存続期間に関して問題が残る可能性があります。これらは関数パラメーターに最もよく使用され、(デフォルトの値渡しとは対照的に)ポインター渡しのCパターンと出力パラメーターを効果的に包含します。
  • イテレータは、コレクション内のいくつかの要素を指す配列のようなポインタです。ポインターをインクリメントしたいときはいつでも、実際にはイテレーターが必要でした。実際には、イテレーターには、nullポインター、存続時間、またはバッファーオーバーランに関する問題がないか、ごくわずかです。
  • スマートポインターは、値のライフタイムセマンティクスを明示的に指定することにより、ライフタイムに関する問題を解決します。状況によっては、未加工のポインターを使用して例外セーフコードを作成することは不可能ですが、スマートポインターを使用すると、これは簡単です。他の点では、生のポインタの完全な柔軟性を保持します-重要なことに、それらはNULLにすることができます。また、_std::unique_ptr_はゼロコストの抽象化であるため、ポインターに明確な所有者がいる場合は常に、スマートポインターを使用すると安全性が確実に向上します。
  • _std::vector_や_std::string_などのSTLタイプは、配列のようなポインターのほとんどの使用法を置き換え、適切なライフタイム管理、適切なコピーを導入し、正しく使用するとバッファーオーバーランが発生する可能性が低くなり、非効率的でさえありません。残念ながら、それらには安全でない_operator[]_がありますが、代わりに境界チェック済みの.at()を使用するオプションがあります。

これらのソリューションは完璧ではありませんが、危険で間違ったコードを書くことをより困難にします。 Cの互換性または(共有ポインターの場合は、ベクターのいくつかの使用シナリオ)オブジェクトサイズを最適化しない限り、この安全性を忘れる理由はありません。参照、イテレーター、スマートポインター、またはコレクションが、生のポインターよりも問題に対するより優れた、より自己文書化された、より正確な解決策ではなかった良い例をまだ見ていません。

これらの安全機能を使用することは専門家ですか?はい:問題を正しく効率的に解決することは専門家です。 segfault、バッファオーバーラン、メモリリークのデバッグは、時間を効率的に使用するものではないため、最初から避けたいと思います。半分以上のIDEまたはエディター(およびC++ 11のauto)を使用すると、これは入力するコードがさらに多くなることはありません。

8
amon

No、それ自体は回避されません。

longそれらが存在するもののリストnotが適切な「生の」ポインタを適切に使用します。

これを短くするには:

  • 生のポインタを使用して所有権をモデル化しないでください(ネイキッドdeleteは避け、ネイキッドnew-> _make_shared_または_make_unique_は友だちにしないでください)
  • 実行not生のポインタを使用して、概念を「受け渡し」非所有参照iff C++参照で十分です。
  • C++参照には、生のポインタを使用する場合にいくつかの制限がありますis OK、多くの場合、より簡単な方法(書き込み、読み取り、および推論)。
  • 生のポインター引数を使用してオプションの関数パラメーターをモデル化することに問題はありません。 f(int* pOptValue)ここで、ユーザーはnullptrを渡して値がないことを示すことができます。 (代わりに、_optional<int>_の代わりに長い議論を始めることができると確信していますが。)
3
Martin Ba