C++ ではローカル変数はスタック上に確保され, スコープを抜けるとデストラクタが 呼ばれて破棄された.
これを利用してリソースの確保と解放を行うのが RAII という技法である.
D では RAII と同様の機能を scope 属性と scope クラスで実現できる.
まずは, C++ での RAII の例
raii.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #include <iostream>
class Foo{
public: // public を使わないとエラーになる
Foo(){
std::cout << "Foo" << std::endl;
}
~Foo(){
std::cout << "~Foo" << std::endl;
}
};
int main(){
std::cout << "{" << std::endl;
{
Foo a;
}
std::cout << "}" << std::endl;
return 0;
}
|
raii.cpp の実行結果は:
[cactus:~/code_d/d_tuts]% ./raii
{
Foo
~Foo
}
D 言語では, インスタンスの破棄はガベージコレクタ (GC) の仕事なのでスコープを抜けてもデストラクタが呼ばれたり破棄されたりしない.
RAII を実現するためには scope キーワードを使う.
raii.d
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | import std.stdio;
class Foo{
private int id;
this(int i){
id = i;
writeln("this: ", id);
}
~this(){
writeln("~this: ", id);
}
}
void main(){
// 明示的に delete
writeln("{");
{
Foo a = new Foo(1);
delete a;
}
writeln("}");
// GC に任せる
writeln("{");
{
Foo a = new Foo(2);
}
writeln("}");
// RAII
writeln("{");
{
scope Foo a = new Foo(3);
}
writeln("}");
}
|
raii.d の実行結果は:
[cactus:~/code_d/d_tuts]% ./raii
{
this: 1
~this: 1
}
{
this: 2
}
{
this: 3
~this: 3
}
~this: 2
“~this:2” と “~this:3” が呼ばれているタイミングの違いに注目
scope 属性を付加したローカル変数は変数宣言時に後から追加するものだった.
これに対して, クラスの設計として RAII を使用しており常に scope 属性を付加すべき ならば, scope クラスとして定義する方法が適している.
scope.d
1 2 3 4 5 6 7 8 9 10 11 | scope class Foo{
}
class Bar : Foo{
}
void main(){
// Foo a = new Foo;
scope Foo b = new Foo;
scope Bar c = new Bar;
}
|
scope クラスは定義時に scope 属性を付加したクラス.
scope クラスは scope 属性付きのローカル変数しか作成できない.
scope 属性は継承される.