- 2009年3月 4日 13:24
- C++/CLI
前回の続き。
CLI は値型と参照型を区別する。一般に値型はスタック、参照型はヒープ上に配置されるが、C++/CLI では参照型であっても C++ と同じようにスタックに置くことができる。正確には「置いたように見せかける」だが、構文的にも意味的にもスタックに置いたオブジェクトと同じように扱える。
List<int> list1; // 参照型なのにスタックに置ける(ように見える)
List<int>* list2 = &list1; // コンパイルエラー: CLI heap 上にあるオブジェクトはポインタで参照できない
List<int>& list3 = list1; // コンパイルエラー: CLI heap 上にあるオブジェクトはトラッキング参照を用いること
List<int>^ list4 = %list1; // % でハンドルを取得
List<int>% list5 = list1; // トラッキング参照は & と同様の振る舞い
構文としてはスタックだが、参照型はあくまで参照型。実際のオブジェクトは CLI ヒープ上に存在するため、間接的に参照する場合はポインタではなくハンドルを使うことになる。つまり * → ^, & → % のルールで自然に振舞うということ。ここまでは問題ない。混乱の元は値型だ。
前回のエントリで、ハンドルは「CLI ヒープ用のポインタ」と述べた。CLI ヒープに確保したインスタンスをポインタで受けることはできないのだから、逆もまた然りと考えて当然である。スタックに置いた値型は CLI ヒープではなく本当にスタック上に置かれるから、ハンドルでは扱えないはずだ。
using namespace System::Drawing;
Point pt1; // 値型をスタックに置く
Point* pt2 = &pt1; // 値型は CLI heap 上にないので、ポインタで参照できる
Point& pt3 = pt1; // & も利用可
Point^ pt4 = %pt1; // コンパイルエラーにならない!
Point% pt5 = pt1; // コンパイルエラーにならない!
ハンドルでは扱えないはずだ......ったが、コンパイルエラーにはならない。どうやらこれがボックス化というもののようだ。ボックス化とは、値型のインスタンスをハンドルで扱うために、インスタンスを一旦 CLI ヒープに「暗黙に」コピーし、そのコピー先を指し示すというものだ。そんな無茶な。
それでは値を間接的に指し示すもの、ポインタとしての役割を果たしていないではないか。ボックス化によって得られたハンドルは、もはや、元のオブジェクトとは無縁の存在というわけだ。実験してみよう。
Point pt(0, 0); // 値型をスタック上に置く
Point* pPt = &pt; // ポインタで参照する
Point^ hPt = %pt; // ハンドルで参照するとボックス化される
pPt->X = 1; // ポインタ経由で X を 1 にする
hPt->Y = 2; // ハンドル経由で Y を 2 にする
cout << "pPt = (" << pPt->X << ", " << pPt->Y << ")\n";
cout << "hPt = (" << hPt->X << ", " << hPt->Y << ")\n";
// [出力結果]
// pPt = (1, 0)
// hPt = (0, 2)
確かに思ったとおりに動作している。駄目だ、ついていけない。Java とか C# とか、平気でボックス化とか言ってる言語で、この手の失敗をしないという自信が持てない。
しかも、こんな書き方もできるらしい。
Point pt; // 値型をスタックに置く
Point^ hPt = pt; // いきなりハンドルで受ける!!
C++ 的な目線で見ると、これは Point* pPt = pt; という風に見えるが、ボックス化様にそんな古い常識は通用しない。何でもアリだ。
ん、ひょっとして、new で native heap に確保したオブジェクトもボックス化様は受け入れてくれるのではないか?
Point* pPt = new Point; // 値型を native heap に置く
Point^ hPt = *pPt; // Point^ hPt = pt; が OK なら、これだって OK だ
delete pPt;
通ったぁ! ボックス化様、万歳。...もうシラネ。
まとめ
- C++/CLI では値型にも参照型にもスタックの構文が使える
- スタックに置いた参照型を参照する場合は * や & ではなく ^ や % を使う
- スタックに置いた値型を参照する場合は *, &, ^, % の全てを使うことができる
- スタックや native heap 上のオブジェクトを ^ や % で参照すると、ボックス化によるコピーを参照するようになる
- Newer: Blender で favicon
- Older: ハンドルとポインタと参照
コメントを表示する前に、このブログのオーナーによる承認が必要になることがあります。コメントが表示されない場合は、承認されるまでしばらくお待ちください。