私は最近、C++ の RAII について知りました。RAII のほとんどの例では、例外の安全性について説明しています。例外がスローされた場合でも、常にリソースを解放する方法。
私が持っている質問は、例外を有効にしていない場合、RAII に価値があるかどうかです。私たちの会社では、arm の組み込みプロジェクトに取り組んでおり、例外はデフォルトでオフになっており、実際にはそれらの必要性は見られません。
すべての答えをありがとう!
C++ の RAII は、はるかに広い概念です。より安全なコードを書くためのイディオムです。RAII のリソース取得部分は、ファイルを開いて後で閉じる、メモリの割り当てと割り当て解除、ロックの取得と解放など、後で終了する必要があることを開始する場所です。RAII は、スマート ポインター、スレッド セーフ (マルチスレッド プログラムでのミューテックス ロックの制御 - http://www.boost.org/doc/libs/1_49_0/doc/html/boost/interprocess/scoped_lock. htmlは RAII の例です)、ファイルとのやり取り、オブジェクトの所有権 (unique_ptr
既に RAII を使用しているようなスマート ポインターを使用する場合) などです。
したがって、RAII は、例外に関係なく、優れた C++ コードで使用する価値があります。
私が持っている質問は、例外を有効にしていない場合、RAII に価値があるかどうかです。
もちろんお得です!例外の安全性は、RAII の 1 つの側面にすぎません。たとえば、もう 1 つは、動的に作成されたインスタンスのリークを回避することです。
私は実際にそれが依存していると思います.C++の仲間の間でここで最も奇妙な答えを競うかもしれません. とはいえ、C++ と C++ の豊富な型システムを既に使用している場合、たとえ例外がなくても、ほとんどの部分で RAII を使用しないのはおかしいと思います。
インターフェイスを設計および使用する際の RAII の欠如の煩わしさ
通常、クライアントが手動および外部で解放/クローズ/破棄する必要がある関数内で割り当てたリソースを返す厄介な関数を作成する必要はありません。同時に、設計を逆にして、クライアントにリソースを割り当てさせ、関数が使用するようにパラメーターでそれらを渡すようにする (クライアントによって割り当てられたバッファーに入力するなど) と、それでも時間がかかる可能性があります。解放/クローズ/破棄するクライアントの責任 (クライアントが少なくとも自分でリソースを作成/オープン/割り当てしたため、おそらく少しクリーンです)。呼び出されたときに関数内で内容が決定される可変長文字列を返す関数を設計するだけでも時間がかかり、RAII を使用するか実装するか、またはその両方が欠けているかのいずれかが常に少し厄介です。
これらは、C でインターフェイスを設計および使用する日常的な迷惑行為であり、スコープ外になったときに自分でクリーンアップするリソースがある場合、C++ で処理する必要はありません。何かにいくつかのコンストラクターとデストラクタを与えるだけでかかる時間は、多くの場合、上記に対処しなければならないという余分な煩わしさをはるかに上回ります。
RAII を欠く危険性
スコープ付きミューテックスのような基本的なものについても同様です。ミューテックスが範囲外になったときに明示的にロックを解除することで、潜在的な将来のバグから確実に保護されます。このような場合、最初にコードを書いてテストするときに、例外を伴わずにバグを個人的に回避するのは簡単かもしれませんが、一部の同僚は、将来のクランチタイム中にその関数に急いでいくつかのステートメントを導入し、mutexreturn
を忘れる可能性があります。unlock
それが明示的に必要な場合。可能性が高いかどうかに関係なく、そのスコープ付きミューテックスがあると便利です。
ダム型システムの利点
しかし、私は C と C++ の両方が大好きで、両方の間を行ったり来たりする奇妙なタイプです。C で行う方がはるかに簡単だと思うことが 1 つあります。それは、ほぼすべてのデータを操作できる低レベルのデータ構造を実装することです。タイプ。C では、vtables と dtors と ctors を持つことができるオブジェクトを含む豊富な型システムがなく、例外がないためです。通常、データ型はビットとバイトとしてmemcpy
あちこちに扱うことができmemmove
、malloc
メモリはあちこちに何でも扱うことができますrealloc
。C で自信を持ってそれを行うことができる理由は、型システムが非常に馬鹿げていて、これらの機能がすべて欠けているからです。
もちろん、私が C で設計したデータ構造は使いにくいものです。それらは型の安全性に欠けており、多くの場合、void*
ポインターを処理し、手動で破棄する必要があります.シャッフルされるビットとバイトとしてデータ型を見ることができます。std::vector
一方、C++ に戻ると、例外の安全性を提供しplacement new
、デストラクタの使用と手動呼び出しなどを提供しながら、適切に拡張可能な配列コンテナーを実装することさえ容易ではありませんstd::allocator
。しかし、std::vector
私が C で設計したどのデータ構造よりも、はるかに便利で安全に使用できます。
したがって、デストラクタやコンストラクタなどを欠く型システムを持つことには、特定の種類の便利さがあり、RAII 準拠のリソースを許可するためにデストラクタとコンストラクタを取得したとしても、C が必ずしも優れている点で改善されるとは限りません。のような既存のあらゆる種類の日常的な C 関数memcpy
は、もはや使用するのに正気ではありません。それらは、C++ の場合と同様に、そのような型システムの下で突然、最も致命的でエラーが発生しやすい関数になります。
したがって、いくつかの低レベルのドメインでは、これらの非常に低レベルのドメインに限られますが、RAII が実際に障害になる可能性があるという議論がなされると思います。一方、例外の有無にかかわらず、他のすべてに非常に有益です。例外により、RAII はほぼ必須になりますが、これらの非常に低レベルのドメインを除けば、RAII は依然として非常に有用です。
自明に構築可能/破壊可能な UDT
ときどきstruct
、C++ のキーワードが構造体を自明に構築可能で破壊可能な単純な古いデータ型 (memcpy
たとえば、安全に回避できる型)に減らしてほしいと思うことがあります。 t。もしそうなら、C を使用する理由は少ないと思います。なぜなら、その時点で、ジェネリック型が自明に構築可能/破壊可能かどうかを判断するために型特性に依存することstructs
なく、そのような のみで動作し、 では動作しないジェネリック コンテナーを作成できるからです。classes
C への魅力は、RAII の欠如ではなく、データ構造に格納して日常的に使用する多くのデータ型がそれを必要としないことを簡単に想定できることです。データ構造を実装するときに、次のことができることを知っておくと便利です。free
N
要素をループしてデストラクタを呼び出すことなく、要素の連続したメモリ ブロック。C++ では、多くの場合、より安全な側で誤りを犯し、ほぼすべてがそれを必要とする (または将来必要になる) と想定して、たとえば、memcpy
誰もどこでも使用することを強く、強く思いとどまらせなければなりません。