C++/CLI(これは古い日記です)

C++/CLIを使ってみた。
これは何というか、すぐには理解できない。謎すぎる。

まず、普通のC++についてだが、C++ではオブジェクトはスタック、ヒープ、いずれにも配置できる。

// class Foo {};
// スタック
Foo foo;
// ヒープ
Foo* foo2 = new Foo;
delete foo2;

スタックに積んだオブジェクトはスコープを抜けるときに破棄される。つまり、デストラクタが呼び出される。
C++にはGCなどというものはないので、ヒープに積んだオブジェクトはdeleteで破棄する必要がある。
つまり、deleteしたタイミングでデストラクタが呼び出される。

C++/CLIでは、まだこの言語の仕様を全然理解していないのだが、参照型クラスみたいなものがあり、gcnewというキーワードを用いて作成した場合には、GCによってメモリが回収されるらしい。

// クラス定義
ref class Bar {};
Bar^ bar = gcnew Bar;

「^」が気持ち悪いが、まあそれは置いておいて、gcnewで確保されたオブジェクトが破棄される時には、ファイナライザ、というものが呼び出されるらしい。これはデストラクタとは別のものだ。

ref class Bar {
    Bar::Bar() {}   // コンストラクタ
    Bar::~Bar() {}  // デストラクタ
    Bar::!Bar() {}  // ファイナライザ
};

従って、GCが適当なタイミングで!Bar()を呼び出す。しかし、このBarは普通にスタックにも積めるようだ。

{
    Bar bar;  // これはスタックに積む、という意味なのか?
}

こうした場合、「}」を抜けた時点で、ファイナライザでなく、デストラクタが呼び出される。
マネージオブジェクトなので、厳密にはスタックに積む、という表現はおかしいだろうが、まあ、見た目にはスタックに積まれているように見える。
実際にはこの時点ではメモリは解放されず、最終的にはGCが適当なタイミングでメモリを解放するらしい。
従って、参照型のクラスでのデストラクタは、純粋にリソース解放用に用いるものということになるらしい。

refとか^とか!とか、謎な記号が増加して鬱陶しいが、このスコープを抜けるタイミングでリソースが解放される、という構文、仕組みは、Java, C#等の言語と比べて優れている。

参考までに、C++のデストラクタを用いたリソース解放の例としては、次のものが有名である。

class Lock {
public:
    Lock::Lock() { mutex_.lock(); }
    Lock::~Lock() { mutex_.unlock(); }
private:
    Mutex mutex_;
};
{
    Lock lock;  // lock()
    // 排他的な処理
}
// unlock()

{}内で排他的な処理を行い、スコープを抜けたときに自動的にlockが解放される。JavaC#で同じことを行おうとすると、もっと複雑になる。

そもそもC++/CLIは、Managed C++(Managed Extension for C++)の問題点を改良した、という立場の言語のようなので、Managed C++を知らない人間からしたら、どこが良くなったのかはわかりにくい。
しかし、Managed C++ではこう書いていたのを、C++/CLIではこうなりますよ、という説明を見ると、Managed C++の読みにくさ(汚さ)だけはわかった。

C++/CLIC++で.NETを扱えるようにするためにC++言語に拡張を加えたという立場のようだが、変更点が多すぎて、C++からC++/CLIを学ぶのは、ゼロからC#を学ぶのと同じくらいの労力を必要としそうだ。まあ.NET言語は、シンタクスが異なるだけで、中身は全部一緒なので、どれか一つを理解すれば、他の.NET言語を覚えるのは、純粋にシンタクスの違いだけということになるのだろうが。

シンタクスが違うだけで同じ.NET言語であるというなら、C#の前に、C++/CLIの存在意義はあるのか?ということになるが、C++の.NET言語(C#, VB.NETとかもろもろ)より優れた部分としては、やはりtemplate, STLの存在が挙げられる。前にも書いたが、Genericsはtemplateの偽者だ。C++/CLIでもSTLは使えるが、これはアンマネージ型しか格納できない。アンマネージ型しか用いない従来のC++プログラミングを行う分にはこれでも問題ないが、マネージ型に対してはGenericsは使えるものの、これはtemplateより非力である。
そこで、マネージ型を扱えるSTLとして、STL.NETというものがあるらしい。これはVisual Studio 2005で導入されるはずだったが、どうも間に合わなかったため、後日配布されるということになったようだ。これが揃えば、C++/CLIは俺の中ではほぼ最強ということになる。

しかしC#の方がCの下位互換性を捨てて一から設計しなおした分、言語が全体としてきれいなので、もしもC#にtemplateが導入されるということになれば、C#の方が優れているということになるかもしれない。まあそれが可能なのかどうかはわからないし、上の方に書いたスコープを用いたリソース解放の問題は残るが。

現在の標準C++は、ISO/IEC14882:1998というもので1998年に定められている。C#C++/CLIもどこかで標準化されている。しかしC++0xという、200x年に標準化される次期C++というものがまた別に存在する。するとC++98からC++/CLIC++0xに分岐してしまうということになって鬱陶しい。
C89もC99とC++に分岐したといえば分岐したみたいな形になったわけで、C++98はC89との95%以上の互換性はあるが、最早C99との互換性はない。C99の_Boolとかいうキーワードの命名がありえねえ、とかまあそういう問題はおいておくとして、C++好きとしては、今後C++がどうなっていくのかは興味深いところである。
しかしC++っぽい言語として、C++98, Java, C#, C++/CLI, D, C++0xと、これだけ増えられると流石にちょっとついていけない気もするが…。

参考: