C++/CLI
C++/CLIは、.NET Frameworkの共通言語基盤 (CLI) に対応し、共通言語ランタイム (CLR) 上で実行されるプログラムを記述するために、マイクロソフトがC++を拡張したプログラミング言語である。前身であるC++マネージ拡張に比べて単純でわかりやすい構文になり、可読性も向上している。Microsoft Visual Studio 2005からサポートが追加された。 C++/CLIは最初のバージョンがEcma Internationalで標準化されている[1]。C++/CLIに対応したコンパイラとしてMicrosoft Visual C++ 2005 (8.0) 以降がある。ほかにもClang上で実装する試みも存在する[2]。 .NET Frameworkだけでなく.NETでも利用可能であり、.NET Core 3.1およびVisual Studio 2019 (16.4) からサポートが追加された[3]が、サポートされるプラットフォームはMicrosoft Windowsのみである[4]。 構文の変化C++マネージ拡張がC++のスーパーセット(上位互換)指向であった[注釈 1]のに対し、C++/CLIは独立した別の言語である[注釈 2]。これにより、特に曖昧な識別子の削除や、.NET固有の機能追加に関連する大きな構文上の変更が入っている。 もっとも大きな構文の違いとしてはnew演算子が挙げられる。C++/CLIでは.NETの参照型のインスタンスを作るための演算子をgcnewに分離した。また、.NET 2.0のジェネリクスに対応する構文も追加された。 C++/CLIも標準C++とは概ね互換性があるが、C++11で追加された 新しい構文機能やキーワードは、C#の影響を受けているものも多い。なお、C++/CLIの独自拡張機能のうち、 Microsoft Windows 8で導入されたWindowsランタイム (WinRT) を利用するコードをC++で効率的に記述できるようにするために、Visual Studio 2012では新たな独自拡張言語としてC++/CX[10]のサポートが追加されたが、このC++/CXもC++/CLIとよく似た構文を採用している。ただしC++/CLIはマネージ言語拡張であるのに対し、C++/CXはネイティブ言語拡張である[注釈 3]。 用途C++/CLIはC++マネージ拡張と同様に、マネージコードとネイティブコード(アンマネージコード)を混在して記述することのできる唯一の.NET言語である[12]。主にC言語やC++のようなネイティブ言語で書かれたコード資産を、C#やVisual Basic .NET (VB.NET) のようなマネージ言語から利用するために使われるが、逆にマネージ言語で書かれたコード資産をC++から利用するための混在コードを記述することも可能である。.NETには他にもP/InvokeやCOM相互運用などの手段も用意されているが、C++/CLIでネイティブコードをラップしてマネージアセンブリ(DLL)を作成する方法は、より細やかな制御を可能とし、.NET言語から直接扱いやすいプログラミングインターフェイスを提供することが可能となる。 反面、.NETアプリケーションコードの効率的な記述能力やRAD対応はC#やVB.NETに劣り、統合開発環境 (IDE) の支援を受けられないことが多い。Visual Studio 2010まではWindows Forms関連のプロジェクトテンプレートがC++/CLI向けにも提供されていたが、Visual Studio 2012では削除された[13]。WPF関連のプロジェクトテンプレートは最初からサポートされていない。C++/CLIは、マネージコードとアンマネージコードの相互運用を行う目的でのみ使用することが推奨されている。 ハンドルマネージ拡張C++には、2種類のポインタが存在した。従来からのC++ポインタである // マネージ拡張C++
#using <mscorlib.dll>
using namespace System;
using namespace System::Collections;
__gc class ReferenceType
{
private:
String* stringVar;
int intArr __gc[];
ArrayList* doubleList;
public:
ReferenceType(String* str, int* pointer, int number) // どれがマネージ型だろうか?
{
doubleList = new ArrayList();
intArr = new int __gc[8];
Console::WriteLine(String::Concat(str->Trim(), number.ToString()));
}
};
// C++/CLI
#using <mscorlib.dll>
using namespace System;
using namespace System::Collections::Generic;
ref class ReferenceType
{
private:
String^ stringVar;
array<int> intArr;
List<double>^ doubleList; // ジェネリック型の構文が追加された
public:
ReferenceType(String^ str, int* pointer, int number) // 区別が容易
{
doubleList = gcnew List<double>();
intArr = gcnew array<int>(8);
Console::WriteLine(str->Trim() + number); // Stringの連結に+演算子が使用可能となった
}
};
追跡参照C++/CLIの追跡参照(トラッキング参照)は値ではなく参照で渡されるハンドルである。これらはC#の 下記のコードは追跡参照の使用例である。仮に、下のコードで {
array<String^>^ arr = gcnew array<String^>(10);
int i = 0;
for each (String^% s in arr)
s = i++.ToString();
}
加えて上記のコードは.NET言語の間でも表現力に差があるという例になる。C#のforeach文では {
string[] arr = new string[10];
for (int i = 0; i < arr.Length; ++i)
arr[i] = i.ToString();
}
C++/CLIには、C#の ファイナライザと自動変数そのほかの変化として、C++/CLIではガベージコレクション時に実行されるファイナライザの構文が!クラス名()となったことが挙げられる。そして~クラス名()は従来のC++と同じ意味のデストラクタとなった。さらに、下の例にあるような新しい構文では、従来のC++と同じくデストラクタは自動的に呼ばれる。共通中間言語 (CIL) 上では、C++/CLIのデストラクタはIDisposableインターフェイスのDisposeメソッドとして実装される。C++/CLIコンパイラがそのようにコンパイルする。このためC++/CLIでも引き続きRAIIが可能である。 // C++/CLI
// デストラクタを定義すると、IDisposableを明示的に指定しなくても、コンパイラが自動的にIDisposableを実装すると判断する。
ref class MyClass // : IDisposable
{
public:
MyClass() {} // コンストラクタ。
~MyClass() {} // デストラクタ。コンパイラによってIDisposable::Dispose()に変換される。
static void Test()
{
{
MyClass x; // ハンドルでなく初期化子も無い:コンパイラがコンストラクタを呼ぶ。
x.ToString();
// コンパイラはブロック全体を包むfinallyを作り、その中で自動変数xのデストラクタを呼ぶコードを自動生成する。
}
MyClass^ user;
try
{
user = gcnew MyClass();
user->ToString();
}
finally { delete user; }
}
protected:
!MyClass() {} // ファイナライザ。Object::Finalize()を直接オーバーライドすることはできない。マネージ拡張C++ではvirtual void Finalize()という構文だった。
};
// C#
class MyClass : IDisposable
{
public MyClass() {} // コンストラクタ。
~MyClass() {} // ファイナライザ(旧称デストラクタ)。Object.Finalize()を直接オーバーライドすることはできない。
public void Dispose() {} // IDisposable.Dispose() メソッドの実装。
public static void Test()
{
using (MyClass x = new MyClass())
{
x.ToString();
}
// コンパイラはusingブロックを抜けるときにx.Dispose()を必ず呼ぶコードを自動生成する。
// つまり以下のコードに等しい。
MyClass user;
try
{
user = new MyClass();
user.ToString();
}
finally { if (user != null) user.Dispose(); }
}
}
演算子の多重定義アンマネージドのC++に関しては演算子の多重定義はおおむね正確に働く。すべての これは、中の文字列が同一ならば、2つの異なる //参照演算子の多重定義の効果
String ^s1 = "abc";
String ^s2 = "ab" + "c";
Object ^o1 = s1;
Object ^o2 = s2;
s1 == s2; // true
o1 == o2; // false
標準的なセマンティクスではネイティブ型や値型、仮に型Tに対しては、従来のC++のようにTやT const&を引数に取る演算子を定義し、参照クラス型Rに対してはハンドルR^を引数に取る演算子を定義することになる。ただ、C++だけのプロジェクトでは、ハンドル型を引数に取る演算子多重定義を使わないようにする、つまり参照クラスに対しても従来のC++の演算子の多重定義方式のように参照 (R const%) を引数に取るという手段も考えられる。そのような例は、演算子ではないがコピーコンストラクタや代入演算子の実装で使われることが考えられる。 脚注注釈出典
関連項目外部リンク |
Portal di Ensiklopedia Dunia