収集された言語のガベージに慣れている人は、C++のメモリ管理を恐れていることがよくあります。 auto_ptr
やshared_ptr
のような、メモリ管理タスクの多くを処理するツールがあります。多くのC++ライブラリはこれらのツールよりも古く、メモリ管理タスクを処理する独自の方法を備えています。
メモリ管理タスクにどのくらいの時間を費やしていますか?
使用しているライブラリのセットに大きく依存していると思います。そのため、どのライブラリに当てはまるか、またそれが改善するか悪化するかを教えてください。
最新のC++では、必要になるまで、つまり主に最適化の目的で、またはコンテキストによって強制される場合(大きな制約のハードウェアを考える)まで、メモリを手動で整理する必要があるまで、メモリ管理について心配する必要はありません。私は生のメモリを操作せずにゲーム全体を書きました。どの言語でもそうであるように、仕事に適したツールであるコンテナの使用についてのみ心配しています。
したがって、プロジェクトに依存しますが、ほとんどの場合、処理する必要があるのはメモリ管理ではなく、オブジェクトのライフタイムのみです。これは、スマートポインターを使用して解決されます。これは、 [〜#〜] raii [〜#〜] から生成される慣用的なC++ツールの1つです。
[〜#〜] raii [〜#〜] を理解したら、メモリ管理は問題になりません。
次に、生のメモリにアクセスする必要がある場合は、「どこでも」ではなく、プールオブジェクトの実装のように、特定のローカライズされた識別可能なコードでアクセスします。
この種のコード以外では、メモリを操作する必要はなく、オブジェクトの有効期間のみを操作します。
「難しい」部分は、RAIIを理解することです。
メモリ管理は子供を怖がらせるために使用されますが、プログラマが注意しなければならないリソースは1種類だけです。ファイルハンドル、ネットワーク接続、OSから取得するその他のリソースについて考えてください。
ガベージコレクションをサポートする言語は通常、これらのリソースの存在を無視するだけでなく、デストラクタを提供しないことにより、これらのリソースを適切に処理することも困難にします。
つまり、要するに、C++開発者の時間の多くがメモリ管理の心配に費やされないことをお勧めします。 クライムの答え が示すように、RAIIのハンドルを取得すると、残りは反射です。
ほとんどありません。 COMのような古いテクノロジでも、非常に短時間で変換する標準ポインタのカスタム削除機能を作成できます。例えば、 std::unique_ptr
は、5行のカスタム削除機能を持つCOM参照を一意に保持するように変換できます。独自のリソースハンドラーを手動で作成する必要がある場合でも、SRPやコピーアンドスワップなどの知識が普及しているため、リソース管理クラスを比較的簡単に作成して、いつまでも使用することができます。
実際には、共有され、一意で、所有権はすべてC++ 11コンパイラに同梱されており、古いコードでも動作するように小さなアダプタを作成する必要があります。
私がC++プログラマーだったとき(かなり前のこと)、私は長い間、メモリ管理のバグを心配して、バグを再現するように修正しようとしました。
モデムC++では、メモリ管理はそれほど問題ではありませんが、大規模なチームの全員がそれを正しく実行することを信頼できますか。費用/時間は次のとおりです。
したがって、「doing」に費やす時間だけではなく、これは大規模プロジェクトではさらに問題になります。
私はboostライブラリとTR1ライブラリをよく使用しています。厳密な意味でのメモリ管理(新規/削除)は問題ではありません。一方、C++でのメモリ割り当ては安価ではなく、これらの空想的な共有ポインタが作成される場所に注意を払う必要があります。ワークスペースを頻繁に使用したり、スタックベースのメモリを操作したりすることになります。一般に、それは実装上の問題ではなく、ほとんどが設計上の問題であると私は言います。
クライアントとしてどれくらいの時間がかかりますか?あなたがそれのこつを得たら、非常に少ない。コンテナーが存続期間と参照を管理する場合、それは非常に簡単です。 imo、それは手動の参照カウントよりもはるかに単純であり、コンパイラーが適切に設計されたタイプセーフシステムで無効な所有権転送を実行するのを便利に防ぐドキュメントとして使用するコンテナーを考慮すると、それは事実上透過的です。
私が(クライアントとして)費やす時間のほとんどは、他のAPIからの型を含めるために費やされるため、プログラムのコンテキスト内で適切に機能します。例:これは私のThirdPartyFontコンテナーであり、これらの機能をサポートし、この方法で破棄を実装し、この方法で参照カウントし、この方法でコピーし、...です。これらの構成要素の多くは配置する必要があり、多くの場合、それらを配置するのに論理的な場所です。それを時間として含めるかどうかは、定義に依存します(とにかく、これらのAPIとのインターフェースをとるときに実装が存在する必要がありますか?)。
その後、メモリと所有権を考慮する必要があります。下位レベルのシステムでは、これは適切であり、必要ですが、物事を移動する方法を実装するには、ある程度の時間と足場が必要になる場合があります。これは低レベルのシステムの要件であるため、私はそれを苦痛とは考えていません。所有権、管理、責任は明らかです。
そのため、不透明タイプを使用するCベースのAPIに向けることができます。コンテナーを使用すると、存続時間の管理と不透明タイプのコピーに関するすべての実装の詳細を抽象化できます。これにより、リソース管理が非常にシンプルになり、時間と欠陥を節約できます。実装を減らします。
これらを使用するのは非常に簡単です。問題(GCから来る)は、リソースの寿命を考慮する必要があることです。間違えた場合、解決するまでに時間がかかることがあります。明示的なライフタイム管理の学習と統合は、比較して理解できるほど複雑です(すべての人が対象というわけではありません)。それが本当のハードルです。ライフタイムの制御と適切なソリューションの使用に慣れれば、リソースのライフタイムの管理は非常に簡単です。それは私の一日の重要な部分ではありません(難しいバグが忍び込んでいない限り)。
コンテナー(自動/共有ポインター)を使用していない場合は、苦痛を訴えているだけです。
私は自分のライブラリを実装しました。それを実装するにはme時間かかりますが、ほとんどの人は再利用します(これは通常は良い考えです)。
あなたは手動でメモリを解放し、ファイルを閉じなければならないようなものですか?そうだとすれば、私が使用した他のほとんどの言語よりも少なく、通常はそれよりも少ないと言えます。特に「メモリ管理」だけでなく「リソース管理」に一般化するとします。その意味で、C++では、手動でのリソース管理に必要なリソースは、JavaやC#など)よりも少ないと思います。
これは主に、リソース(メモリなど)の破棄を自動化するデストラクタが原因です。通常、C++でリソースを手動で解放または破棄する必要があるのは、vlowレベルのデータ構造(ほとんどの人が行う必要のないもの)を実装している場合、または少し時間を費やしているだけでC APIを使用している場合のみです。手動で解放/破棄/クローズする必要があるCリソースをRAII準拠のC++ラッパーにラップします。
もちろん、ユーザーが画像編集ソフトウェアで画像を閉じるように要求した場合は、コレクションなどから画像を削除する必要があります。しかし、うまくいけば、このコンテキストで重要な種類の「メモリ」または「リソース」管理としてカウントされません。その時点でそのイメージに関連付けられたメモリを解放したい場合は、どの言語でもほとんど必要になるためです。しかし、繰り返しますが、コレクションから画像を削除するだけで、残りは画像デストラクタが処理します。
一方、JavaまたはC#)と比較すると、ファイルを手動で閉じたり、ソケットを手動で切断したり、オブジェクト参照をnullに設定してガベージコレクションを実行したりする必要があることがよくあります。 。私に尋ねれば、これらの言語では手動のメモリとリソースの管理がはるかに多くなります。C++では、ミューテックスロッカーが自動的に行うので、ミューテックスを手動でunlock
する必要さえないことがよくあります。たとえば、ミューテックスはスコープから外れます。たとえば、C++では次のようなことをする必要はありません。
_System.IO.StreamReader file = new System.IO.StreamReader(path);
try
{
file.ReadBlock(buffer, index, buffer.Length);
}
catch (System.IO.IOException e)
{
...
}
finally
{
if (file != null)
file.Close();
}
_
C++でファイルを手動で閉じるなどの操作を行う必要はありません。結果としてスコープから外れた場合でも、通常または例外的な実行パスであった場合でも、スコープから外れた瞬間に自動的に閉じられます。 _std::vector
_などのメモリ関連リソースについても同様です。上記のfile.Close()
のようなコードは、特にfinally
ブロックのコンテキストでは、C++に関する考え方全体がそれを自動化します。
手動のメモリ管理に関しては、Cが最大、Java/C#が中程度、C++が最小を必要とすると思います。 C++は習得が非常に難しい言語であるため、C++を使用することに少し恥ずかしがる理由はたくさんありますが、メモリ管理はその1つにすべきではありません。それとは逆に、私は実際、それがこの1つの側面で最も簡単な言語の1つだと思います。
もちろん、C++では、メモリを手動で割り当て、_operator delete/delete[]
_を呼び出して手動でメモリを解放できます。また、malloc
やfree
などのC関数を使用できます。しかし、それは古代のスタイルのコーディングプラクティスであり、Stroustrupが非常に早い段階から用語を作成する前にRAIIを提唱していたため、人々が功績を認めるずっと前に廃止されたと思います。したがって、「現代のC++」がリソース管理を自動化すると言っても、それがずっと目的であるはずだったので、公平だとは思いません。そうでなければ、例外安全性を実際に得ることはできません。 90年代前半の多くの見当違いの開発者がC++をオブジェクトでCのように使用しようとし、例外処理を完全に無視することがよくありました。 C++を実質的に常に意図された方法で使用する場合、メモリ管理は完全に自動化されており、通常は手動で処理する必要のある(または処理する必要がある)ものはまったくありません。
チームのシニアテクニカルリードによって異なります。一部の会社(私の会社を含む)では、スマートポイナーと呼ばれる概念がありません。ファンシーと見なされます。したがって、人々は単に削除をあちこちに配置し、2か月ごとにメモリリークを修正する必要があります。削除ステートメントの新しい波がどこにでも届きます。したがって、会社とそこで働く人々の種類に依存します。