web-dev-qa-db-ja.com

C ++でコーディングするときにポインターが推奨されないのはなぜですか?

C++を使用する場合はポインターを使用しないことをお勧めします。 C++を使用しているときにポインターがこのように悪い考えになるのはなぜですか。ポインターの使用に慣れているCプログラマーにとって、C++のより良い代替手段とアプローチは何ですか?

45
Joshua Partogi

通常のポインタの代わりに スマートポインタ を使用する必要があることを意味します。

コンピュータサイエンスでは、スマートポインタは、自動ガベージコレクションや境界チェックなどの追加機能を提供しながら、ポインタをシミュレートする抽象的なデータ型です。これらの追加機能は、効率を維持しながら、ポインターの誤用によって引き起こされるバグを減らすことを目的としています。スマートポインタは通常、メモリ管理の目的で、それらが指すオブジェクトを追跡します。

ポインタの誤用は、バグの主な原因です。ポインタを使用して記述されたプログラムで実行する必要がある定数の割り当て、割り当て解除、参照は、メモリリークが発生するリスクをもたらします。スマートポインターは、リソースの割り当てを自動的に解除することでメモリリークを防止しようとします。たとえば、スコープ外になったために、オブジェクトへのポインター(または一連のポインターの最後のポインター)が破棄されると、指定されたオブジェクトも破棄されます。

C++では、ガベージコレクションとメモリリークの防止に重点が置かれます(2つ挙げると)。ポインターは言語の基本的な部分であるため、プログラムの最も小さな部分を除いて、ポインターを使用しないことはほとんど不可能です。

58
jmq

私は論争を発表した人なので 「f * ckingポインターを使用しないでください」 ここでコメントする必要があると思います。

まず第一に、それは論争として明らかに極端な視点を表しています。 は確かに(生の)ポインタの正当な使用法です。しかし、私(および多くのプロのC++プログラマー)は、これらのケースは非常にまれであることを主張しています。しかし、私たちが実際に意味するのは次のとおりです。

最初:

生のポインタは、いかなる状況でもメモリを所有してはなりません。

ここで、「自分のメモリ」とは基本的に、ある時点でそのポインタに対してdeleteが呼び出されることを意味します(ただし、それよりも一般的です)。 このステートメントは絶対的に安全に解釈できます。only例外は、独自のスマートポインタを実装する場合です(または他のメモリ管理戦略)。そして、そこにも、通常はstill低レベルでスマートポインタを使用する必要があります。

これの理論的根拠は非常に単純です。メモリを所有する生のポインタがエラーの原因をもたらします。また、これらのエラーは既存のソフトウェアで多く発生します。メモリリークと二重削除-どちらも、リソースの所有権が明確でないことの直接的な結果です(ただし、反対方向に進みます)。

この問題は、生のポインターの代わりにスマートポインターを使用するだけで、実質的に無料で完全に排除できます(注意:もちろんこれにはまだ考えが必要です;共有ポインターはサイクルにつながる可能性がありますしたがって、メモリリークが発生しますが、これは簡単に回避できます)。

第二:

C++でのポインタのほとんどの使用は不要です。

他の言語とは異なり、C++は値のセマンティクスを非常に強力にサポートしており、ポインターの間接参照を必要としません。これはすぐには実現されませんでした–歴史的に、C++はCでのオブジェクト指向を容易にするために発明され、ポインターで接続されたオブジェクトグラフの構築に大きく依存していました。しかし、最近のC++では、このパラダイムがめったに最良の選択となることはめったにありません。最新のC++のイディオムは、ポインターをまったく必要としないことがよくあります。それらは、ポインタではなくvaluesを操作します。

残念ながら、このメッセージはまだC++ユーザーコミュニティの大部分でキャッチされていません。その結果、記述されたC++コードのほとんどは、余分なポインターが散らばっているため、コードが複雑になり、速度が遅く、障害があり、信頼性が低くなります。

最新のC++を知っている人にとっては、anyポインター(イテレーターとして使用する場合を除いて、スマートまたはロー)がほとんど必要ないことは明らかです。結果のコードは短く、複雑でなく、読みやすく、多くの場合より効率的で信頼性が高くなります。

97
Konrad Rudolph

生のメモリへのアクセスや割り当て後のクリーンアップなど、ポインタを使用することのより気まぐれな側面を隠す抽象化が利用できるからです。スマートポインター、コンテナークラス、およびRAIIのようなデザインパターンにより、生のポインターを使用する必要性が減少します。とは言っても、他の抽象化と同様に、それらが実際にどのように機能するかを理解してから、それらを超えて移行する必要があります。

15
Ed S.

比較的単純に、Cの考え方は「問題が発生した場合はポインタを使用する」です。これは、C++の初期のメンバーポインタでも、C文字列、関数ポインタ、イテレータとしてのポインタ、ポインタからポインタ、空のポインタで確認できます。

ただし、C++では、これらのタスクの多くまたはすべてに値を使用できます。関数の抽象化が必要ですか? std::function。それは関数である値です。 std::string?それは値であり、それは文字列です。 C++全体で同様のアプローチを見ることができます。これにより、人間とコンパイラの両方にとってコードの分析が非常に簡単になります。

11
DeadMG

理由の1つは、ポインターの適用が広すぎることです。これらは、コンテナーを介した反復、関数への受け渡し時の大きなオブジェクトのコピー、重要なライフタイム管理、メモリ内のランダムな場所へのアクセスなどを回避するために使用できます。また、1つの目的で使用すると、他の機能が利用可能になりますすぐに独立して意図します。

正確な目的のためのツールを選択すると、コードがよりシンプルになり、意図がより見やすくなります-反復のためのイテレーター、寿命管理のためのスマートポインターなど。

10
maxim1000

すでにリストされている理由に加えて、明らかな理由があります。それは、より良い最適化です。エイリアシング分析はポインター算術の意味で非常に複雑ですが、参照はオプティマイザにヒントを与えるので、参照のみが使用されている場合、より深いエイリアシング分析が可能です。

3
SK-logic

@ -mmquigleyポインタとポインタ演算によって示されるメモリリークのリスクに加えて、ポインタはメモリ内のあらゆる場所を指すことができますは「バグを見つけにくい」と「セキュリティの脆弱性」を引き起こすため、問題があると考えることができます。

それが、C#とJavaでほとんど見捨てられた理由です。

2
k3b