書けば書くほどABとどこも似ていないとしか思えなくなりますが、まだまだ続きます。そんなC++/CLI第3回目は、前回に続きC++/CLIの何でもありな具合を見せてくれる追跡参照を取り上げます。


ご存知のとおり、C++には参照というものがあります。

int i;
int& r = i;
 
int* p = new int;
int& r2 = *p;

ポインタがハンドルになったように、参照に対応するものとして追跡参照というものが導入されました。%を使います。

Object^ obj = gcnew Object;
Object% ref = *obj;
 
Object o; //Objectは参照型ですがこんなことができます。詳しくは次回
Object% r = o;
Object^ h = %o;
Object^ h2 = %r;

ハンドルから参照を得る演算子は*であるのに対し、左辺値からハンドルを得る演算子が%になっている点は、一見奇妙ですが、きちんとわけがあります。しかし、それは次回以降の話です。

追跡参照とは、CLIのガベージコレクションでは、インスタンスのメモリ位置が移動されることがあるので、GC時にそれを追跡するということに由来します。ハンドルも追跡ハンドルと呼ばれることがあります。

もちろん追跡参照は値型に対しても区別無く使用できます。更にスタックやネイティブヒープ上にあるものなど、GC対象外のインスタンスを参照することすらできます。このように何でもありです。

int i;
int% r1 = i;
 
int& r = i;
int% r2 = r;
 
int *p = new int;
int% r3 = *p;
 
int^ h = gcnew int;
int% h4 = *h;

前に書いたようにハンドルではボックス化されましたが、追跡参照ではそうなりません。ポインタ・ハンドルと違い、追跡参照は正にC++参照のマネージ版に徹していると感じます。

#include <iostream>
 
int main()
{
  int i = 1, j = 7;
 
  int^ h = i;
  int% r = j;
 
  h = 11;
  r = 77;
 
  std::cout << *h << ' ' << r << std::endl;
  std::cout << i << ' ' << j << std::endl;
}</code>

ハンドルへの参照ということも可能です。

#include <iostream>
#include <string>
 
void f(std::string*& s) //sは、「std::stringへのポインタ」への参照
{
  s = new std::string("hello, ");
}
 
void f(System::String^% s) //sは、「System::Stringへのハンドル」への追跡参照
{
  s = gcnew System::String("world.");
}
 
int main()
{
  std::string* s;
  f(s);
  std::cout << *s << std::flush;
  delete s;
 
  System::String^ s2;
  f(s2);
  System::Console::WriteLine(s2);
}

ハンドルへの参照型が関数の引数で用いられている場合、C#での参照型に対するref/out引数に相当します:方法 : out パラメータを指定する – MSDNライブラリ。なんだかんだいって、これが追跡参照の典型的な利用法の最たるものだと思います。


とまあ、きりがなくなるのでここら辺にしておきます。

スポンサード リンク

この記事のカテゴリ

  • ⇒ C++/CLI (3) 追跡参照 〜いつも追跡するとは限らない〜