RAII is Overrated
Software & Engineering
by Carlos Oliveira
RAII is Overrated
RAII は過大評価されている
In most discussions of the advantages of C++, an item that is frequently raised is the
support for automatic destruction. This mechanism, which is used to reclaim resources
used by a single object at the end of its scope, is the basis for a programming style
popularly known among C++ users as RAII (resource acquisition is initialization).
C++ の advantages に関する議論のほとんどにおいて、頻繁に話題となるのが自動破棄 (automatic
destruction) のサポートです。このスコープの最後終端で単一のオブジェクトによって使用された
オブジェクトを reclaim するのに使われている仕組みは、C++ ユーザーの間で RAII (resource
acquisition is initialization) として広く知られているプログラミングスタイルに基づいています。
The main point of this article is to show that, while RAII is a useful idiom in a
complex language such as C++, it really doesn't provide much help in other, simpler
languages. Even in C, which is a lower level language, the automatic destruction
mechanism doesn't provide much that couldn't be supported by the programmer itself,
sometimes with less surprises and higher performance.
本 article の main point は RAII が C++ のような複雑な言語において有用なイディオムである一
方で、他のもっと単純な言語でも大いに手助けになるようなことを提供してなどいなことを明確にす
ることです。より (ハードウェアよりで) 低水準の言語である C においてさえ、
the automatic destruction mechanism doesn't provide much that couldn't be supported by the
programmer itself, sometimes with less surprises and higher performance.
Is Automatic Destruction Necessary?
自動破棄は必要なのでしょうか?
The first issue that needs to be answered is if automatic destruction is a necessary
feature in general. One of the ways we can answer this questions is looking at the
decisions made by the designers of more recent languages. This may not give us a
complete answer, but it shows at least an indication of usefulness for a particular
feature.
回答する必要のある issue で最初に取り上げるのは自動破棄が一般的に必要な機能かどうかと
いうものです。この疑問に対して回答する方法の一つが、より新しい言語の設計者たちの判断が
どうであったかを見るというものです。これはわたしたちに完全な回答を与えてはくれませんが、
少なくとも特定の機能に対する有用性の指針を show します。
In fact, there are many languages such as Java, Python, Javascript, and Rubi that don't
have an idiom for RAII. Moreover, among the communities of users for these languages
there is no feeling that there is a particular need to add such a feature. This
includes both older languages such as Smalltalk as well as languages more recent than
C++, such as Javascript.
事実、Java や Python、JavaScript、Ruby などのように RAII のためのイディオムを持っていない
言語はたくさんあります。加えて、これらの言語のユーザーによるコミュニティの間では RAII の
ような機能を追加しようという機運はありませんでした。これには Smalltalk のように古くからあ
る言語と、JavaScript のように C++ よりも新しい言語との両方が含まれます。
The main reason most languages don't require a RAII idiom is that they don't need to
manage memory manually. Memory is the single most common resource guarded by C++
classes, a resource that needs to be released every time an object is discarded.
ほとんどの言語が RAII イディオムを必要としていない主な理由は手作業によるメモリ管理が不要
だということです。メモリーは C++ のクラス群によって guard される single most common resource
であり、オブジェクトが破棄 (discard) される度に解放の必要がある resouce です。
Another reason for having automatic destruction in C++ is that is provides a decentralized
way to manage resources for any number of classes. Compared to C, a C++ program needs to
define a lot more user defined data types (classes) in order to do its work. The
interaction between features such as the STL and the inheritance mechanism require that a
lot of classes be created to represent programmatic concepts — no wonder why C++ programs
feel the need to have a mechanism for cleaning up the resulting mess.
C++ で automatic destruction を持つもう一つの理由は、任意個のクラスのためのリソースを
管理する decentralized way を提供するためです。C と比較したとき、C++ のプログラムはそ
の動作のために格段に多くのユーザー定義型(クラス)の定義を必要とします。STL のような機能
と継承機構の間の interaction はプログラム的なコンセプトを表現するために作り出されたた
くさんのクラスを要求します。
C++ のプログラムが resulting mess をクリーンナップするための
機構を持つ必要があると感じられるのに不思議はありません。
Disadvantages of Destructors
デストラクターの欠点
One of the problems with automatic destructors in C++ is that they require a lot of
run-time overhead. Every object in every class needs to call a destructor, even if it
does nothing (which is often the case). It is true that an optimized compiler can
eliminate the call to the destructor in some cases. However, when we have virtual
destructors this becomes very difficult, since the compiler has no way to know exactly
what destructor will be called through the virtual table.
C++ における自動破棄に伴う問題のひとつが実行時のオーバーヘッドが避けられないということ
です。デストラクターで何もすることがなかったとしても、すべてのクラスのすべてのオブジェ
クトはデストラクターを呼び出さなければなりません。最適化コンパイラーが一部のケースにお
いてデストラクターの呼び出しを削除する可能性があるのは本当です。しかしながら、仮想デス
トラクターを持っていた場合、そのような最適化は非常に難しいものとなります。なぜなら、コ
ンパイラーはデストラクターが仮想テーブルを通じて呼び出されることを正確に知る手段を持っ
ていないからです
When a system has a large number of objects with virtual destructors, which is a common
case on object oriented systems, the result is that destructors are called all the time
for no good reason.
オブジェクト指向システムでは一般的なケースですが、あるシステムに仮想デストラクターを持った
大量のオブジェクトがあったときの結果はすべてのデストラクターが good reason を持たずに呼び
出されるというものです
A more subtle problem with the RAII idiom is that, while it solves some of the issues
of resource management, it doesn't address the most difficult cases. The only
instance where RAII works well is when objects are allocated in the stack. For these
allocations, destructors are guaranteed to be called at the end of the scope, even if
an exception happens before the end of the scope is reached.
RAII イディオムに関連するより微妙な問題は、それがリソース管理の問題の一部を解決する一方で
もっとも困難なケースに対処していないというものです。RAII がうまくいく唯一の事例はオブジェ
クトがスタックに割り当てられているときです。これらの割り当てに対しては、スコープの終端に
到達する以前に例外が発生したとしてもデストラクターがそのスコープの終端で呼び出されることが
保証されています。
However, C++ has two more memory allocation models: static-allocated and
heap-allocated memory. Static memory can be a problem for the implementation of the
singleton pattern. It is well known that objects created on static memory will not be
destructed correctly. Even when they may be destructed, there are no guarantees.
Therefore, static allocation of objects should be dealt with special care, and
destructors cannot be of much help in this case.
しかし、C++ にはこれに加えて static-allocated memory と heap-allocated memory というさらに
二つのメモリー割り当てモデルがあります。static memory は singleton パターン実装の際に問題と
なる可能性があります。static memory 上に生成されたオブジェクトが正しく destruct されないの
はよく知られています。それらは destruct されたとしてもなんの恩恵もありません。したがってオ
ブジェクトの static allocation は特別なケースとして扱うべきであり、デストラクターはこのケー
スにおいてそれほど助けになりません。
The other, much more important case of memory allocation is heap-based allocation. It
is important because in an object oriented language the objects use have distinct life
times. Therefore, one should be able to allocate them and pass references around until
they are not needed anymore. The standard way of doing this is allocating memory on
the heap and returning that memory to the pool only when the object is done.
そのほかの、ずっと重要なメモリー割り当てのケースとはヒープベースの割り当てです。これが重要
なのは、オブジェクト指向言語においてオブジェクトはそれぞれが異なる生存期間を持つようになっ
ているからです。したがって一度オブジェクトを割り当てたならば、そのリファレンスをオブジェク
トがもはや必要なくなるまで持ちまわれるようになっているべきなのです。これを行う標準的な方法
が、ヒープ上のメモリーに割り当ててそのメモリーを返すというものです。
Now, the problem with heap allocated memory is that destructors will be of no help in
that case. They will not be called automatically when an object is not needed. If a
programmer doesn't take care of cleaning heap-allocated memory, it will just leak
when no more references to it exist in memory.
さて、このヒープに割り当てられたメモリーについての問題とはデストラクターがこういったケースの
手助けをしないということです。あるオブジェクトが必要なくなったときにデストラクターが自動的に
呼び出されたりはしません。プログラマーがヒープに割り当てられたメモリーに対して注意を払わなか
ったりしたら、メモリーに存在しているものに対する参照がなくなった時点でリークが起きます。
To avoid this, programmers are required to call delete (or delete[]) to make sure that
the object is properly cleaned. Thus, programmers are ultimately the ones responsible
for this difficult task. If you think of the implementation in terms of C (and other
lower level code), in this case the destructor is just a function that needs to be
called in the object. There is nothing special in it other then the syntax, which uses
the keyword delete.
リークを起こさないよう、確実にオブジェクトを完全に消去するためにプログラマーは
delete (もしくは delete[]) を呼び出すことを要求されています。したがって、プログラマーはこの
困難な task に対して resposible な ultimetely the ones なのですもし C の環境 (あるいはより
低水準のコード) でこの実装を考えるのならば、デストラクターとはオブジェクト中で呼び出す必要の
ある関数に過ぎません。delete というキーワードを使っている構文以外は特に特殊なものでもありま
せん。
In fact, the destructor itself is a non-impressive function, with some serious
drawbacks. First, it receives no arguments and returns no value. So, for example,
there is no way to signal an error condition other than throwing an exception. And we
already know that a destructor shouldn't throw exceptions, which means that it cannot
do anything very reliably.
実際、デストラクターそれ自身はいくつかの深刻な drawbacks を抱えた non-impressive な関数です。
第一に、デストラクターは引数を取らないし、また値を返しません。
一例を挙げると、このために例外を送出する以外にエラー状態を知らせる手段がありません。
また、デストラクターは very reliably なことは一切できないことを意味するような
例外を送出すべきでないことをわたしたちはすでに理解しています。
What this amounts to is a system that is far less powerful than necessary to maintain
a first class object oriented system.
what this amounts to は
a first class object oriented system を扱うのに必要なものよりも
far less powerful なシステムです。
Conclusion
結論
RAII is preached by some as the most important feature of C++ for resource handling.
RAII は一部の人たちが最も重要な C++ 向けのリソースハンドリングの機能として主張 (preached)
しています。
However, as we have seen above, it has lots of problems that make it much less appealing
than initially thought.
しかしこれまで見てきたように、RAII には当初考えていたよりも多くの問題があります。
For example, destructors work well only when the memory class used by the object is stack
based. Other allocation classes will result in memory leaks and in manual work for programmers.
たとえば、デストラクターはオブジェクトが使っているメモリークラスがスタックベースのとき
にのみうまく動作します。他のアロケーションクラスでは結果としてメモリーリークを起こし、
プログラマーの manual work が行われることになります。
Even the work performed on behalf of stack allocated objects is nothing more than syntactic
sugar. While tedious, it is not hard to add a matching clean up procedure at the end of the
scope where an object is used, when necessary. If no exceptions are used in code (which is
a common case even on C++), this is all one needs to have correct programs.
スタックに割り当てられたオブジェクトのために行っている作業は構文糖 (syntactic sugar) 以
上のものではありません。退屈なものではありますが、必要なときにオブジェクトが使われたそ
のスコープの終端で clean up の手続きのマッチングを追加するのは難しいことではありません。
コード中で例外が使われていなければ (これは C++ でもよくあることです)、これこそが正しい
プログラムを得るために必要なことなのです。
Published on June 15th, 2011
Copyright © Carlos Oliveira · All rights reserved.