結局1週間経ってしました。それはともかく、前回書いたようにC++/CLIの型の機構は.NETのそれとはかなり異なっています。今回は値型を取り上げます。

CLIにおいて、値型は、ローカル変数なら通常スタック上に割り当てられる上、引数・戻り値は値渡しされるので、基本的にガベージコレクションの世話にはなりません。そのためか、C++/CLIでは値型オブジェクトをどこに置くかということがかなり自由です

例としてSize構造体を取り上げます。

#include <memory>
#using <mscorlib.dll>
#using <System.Drawing.dll>
 
using System::Drawing::Size;
 
struct Foo
{
  Size c; //ネイティブクラスのメンバ
};
 
int main()
{
  HANDLE const hHeap = GetProcessHeap();
 
  //スタック上の自動変数
  Size s;
 
  //ネイティブヒープ
  Size* p = new Size;
  delete p;
 
  std::auto_ptr<Size> ap(new Size);
 
  Size* p2 = static_cast<Size*>(HeapAlloc(hHeap, HEAP_ZERO_MEMORY, sizeof (Size)));
  HeapFree(hHeap, 0, p2);
 
  //マネージヒープ(ボックス化)
  Size ^h = gcnew Size;
 
  //暗黙のボックス化
  Size^ h2 = s;
  System::Object^ o = s;
  System::Object^ o2 = *ap;
}

マネージヒープはCLIが管理しGCの対象になるメモリ確保に使われるヒープで、ネイティブヒープはそうでないmalloc関数やnew演算子のメモリ確保に用いられるヒープの総称です。このように値型のオブジェクトは、どこにでも置けてしまいます。


ところで、C#でボックス化と言えば、殆どの場合object型にすることでした。ほかにもボックス化したオブジェクトを保持できる型はありますが、いずれにしてもアップキャストです。しかしC++/CLIの場合少なくともソースコード上は基の型を保ったままボックス化できます。

Size^ h = gcnew Size;
Size s = *h;

これは型安全で、ボックス化された値を取り出すのにダウンキャストする必要が無いということが利点となるようです。こういうことができるのはどちらかというと参照型との直行性を保つための意味合いが大きいと個人的には思います。


なお、「ボックス化」とは、マネージヒープに新しいオブジェクトを割り当てて、基となるオブジェクトのコピーを作る操作です。ポインタなどと違い、基となるオブジェクトを参照する訳ではないのです。

#include <iostream>
 
int main()
{
  int i = 1;
 
  int^ h = i;
  *h = 42;
 
  std::cout << i << std::endl;
}

このコードをコンパイル・実行すると、1が出力されます。これが問題となる機会は少ないような気がしますが、だからこそうっかりしないように気を付けたいものです。


スポンサード リンク

この記事のカテゴリ

  • ⇒ C++/CLI (2) 値型よどこにでも