Ref クラスと構造体 (C++/CX)
C++/CX は、ユーザー定義された "ref クラス"および "ref 構造体" と、ユーザー定義された "値クラス" および "値構造体" をサポートします。 これらのデータ構造体は、C++/CX が Windows ランタイム型システムをサポートするための主コンテナーです。 それらのコンテンツは、ある特定の規則に従ってメタデータに出力されます。これにより、それらのコンテンツを C++ やその他の言語で記述された Windows ランタイム コンポーネントとユニバーサル Windows プラットフォーム アプリとの間で渡すことができるようになります。
ref クラスまたは ref 構造体には、次のような固有の特徴があります。
名前空間内部で宣言される必要があり、名前空間のスコープ、およびその名前空間内で public または private のアクセシビリティを持つことができます。 パブリック型だけがメタデータに出力されます。 入れ子になったパブリック 列挙型 クラスを含め、入れ子になったパブリック クラス定義は使用できません。 詳細については、「名前空間と型の参照範囲」を参照してください。
メンバーとして、ref クラス、値クラス、ref 構造体、値構造体、null 許容の値構造体などの C++/CX を格納できます。 これには、
float64
やbool
などのスカラー型も含まれる場合があります。 また、パブリックでない限りstd::vector
やカスタム クラスなどの標準の C++ 型が含まれる場合もあります。 C++/CX コンストラクトはpublic
、protected
、internal
、private
、またはprotected private
のアクセシビリティを持つことができます。public
またはprotected
のすべてのメンバーは、メタデータに出力されます。 標準 C++ の型は、private
、internal
、またはprotected private
のアクセシビリティを持つ必要があります。これにより、それらの型がメタデータに出力されることが防止されます。1 つ以上の インターフェイス クラス または インターフェイス構造体を実装することができます。
1 つの基底クラスから継承することができ、基底クラス自体に追加の制限があります。 パブリック ref クラス階層構造での継承には、private ref クラスでの継承によりも多くの制限があります。
generic として宣言することはできません。 private アクセシビリティがある場合、テンプレートになることができます。
その有効期間は、自動参照カウントによって管理されます。
宣言
次のコード片は Person
ref クラスを宣言します。 C++ の標準の std::map
型がプライベート メンバーで使用され、Windows ランタイム IMapView
インターフェイスがパブリック インターフェイスで使用されていることに注意してください。 また、"^" が参照型の宣言に追加されることにも注意してください。
// #include <map>
namespace WFC = Windows::Foundation::Collections;
namespace WFM = Windows::Foundation::Metadata;
[WFM::WebHostHidden]
ref class Person sealed
{
public:
Person(Platform::String^ name);
void AddPhoneNumber(Platform::String^ type, Platform::String^ number);
property WFC::IMapView<Platform::String^, Platform::String^>^ PhoneNumbers
{
WFC::IMapView<Platform::String^, Platform::String^>^ get();
}
private:
Platform::String^ m_name;
std::map<Platform::String^, Platform::String^> m_numbers;
};
実装
このコード例は、 Person
ref クラスの実装を示しています。
#include <collection.h>
using namespace Windows::Foundation::Collections;
using namespace Platform;
using namespace Platform::Collections;
Person::Person(String^ name): m_name(name) { }
void Person::AddPhoneNumber(String^ type, String^ number)
{
m_numbers[type] = number;
}
IMapView< String^, String^>^ Person::PhoneNumbers::get()
{
// Simple implementation.
return ref new MapView< String^, String^>(m_numbers);
}
使用方法
次のコード例は、クライアント コードで Person
ref クラスを使用する方法を示しています。
using namespace Platform;
Person^ p = ref new Person("Clark Kent");
p->AddPhoneNumber("Home", "425-555-4567");
p->AddPhoneNumber("Work", "206-555-9999");
String^ workphone = p->PhoneNumbers->Lookup("Work");
また、スタック セマンティクスを使用して ref クラスのローカル変数を宣言することもできます。 メモリはまだ動的に割り当てられますが、このようなオブジェクトはスタック ベースの変数のように動作します。 1 つの重要な違いは、スタック セマンティクスを使用して宣言されている変数に追跡参照 (%) を割り当てることができないことです。これにより、関数が終了したときに参照カウントが必ず 0 にデクリメントされます。 この例では、基本 ref クラス Uri
、およびスタック セマンティクスでそれを使用する関数を示します。
void DoSomething()
{
Windows::Foundation::Uri docs("http://docs.microsoft.com");
Windows::Foundation::Uri^ devCenter = docs.CombineUri("/windows/");
// ...
} // both variables cleaned up here.
メモリ管理
ref クラスは、 ref new
キーワードを使用して動的メモリに割り当てます。
MyRefClass^ myClass = ref new MyRefClass();
オブジェクトへのハンドル演算子 ^
は "ハット" として知られ、基本的には C++ スマート ポインターです。 これが指すメモリは、最後のキャレットがスコープ外に出ると自動的に破棄されるか、明示的に nullptr
に設定されます。
定義上、ref クラスには、参照セマンティクスがあります。 ref クラスの変数を代入した場合、代入されるのはコピーされるハンドルであり、オブジェクト自体ではありません。 次の例では、代入後に myClass
と myClass2
の両方が同じメモリ位置を指します。
MyRefClass^ myClass = ref new MyRefClass();
MyRefClass^ myClass2 = myClass;
C++/CX ref クラスをインスタンス化する場合、コンストラクターを呼び出す前にメモリがゼロ初期化されます。したがって、プロパティを含む個々のメンバーをゼロ初期化する必要はありません。 C++/CX クラスが Windows ランタイム C++ ライブラリ (WRL) クラスから派生している場合、C++/CX 派生クラスの部分だけがゼロ初期化されます。
メンバー
ref クラスは、 public
、 protected
、および private
の関数メンバーを格納できます。 public
と protected
のメンバーだけが、メタデータに出力されます。 入れ子になったクラスおよび ref クラスは許可されますが、 public
になることはできません。 public フィールドは使用できません。public データ メンバーはプロパティとして宣言する必要があります。 プライベートまたはプロテクトの内部データ メンバーは、フィールドである場合があります。 既定では、ref クラスでは、すべてのメンバーのアクセシビリティは private
です。
ref 構造体は ref クラスと同じですが、既定ではメンバーのアクセシビリティは public
です。
public
の ref クラスまたは ref 構造体は、メタデータに出力されますが、他のユニバーサル Windows プラットフォーム アプリおよび Windows ランタイム コンポーネントから使用できるようにするには、public または protected コンストラクターが少なくとも 1 つは必要です。 public コンストラクターを持つ public ref クラスは、アプリケーション バイナリ インターフェイス (ABI: Application Binary Interface) を介してさらに派生されることを防ぐために sealed
としても宣言される必要があります。
Windows ランタイム型システムが定数をサポートしていないため、パブリック メンバーを const
として宣言することはできません。 静的なプロパティを使用して、定数値を持つパブリック データ メンバーを宣言することができます。
public の ref クラスまたは構造体が定義されると、コンパイラは必要な属性をクラスに適用し、その情報をアプリの .winmd ファイルに格納します。 ただし、public アンシールドの ref クラスを定義するときは、Windows::Foundation::Metadata::WebHostHidden
属性を手動で適用して、クラスが JavaScript で記述されたユニバーサル Windows プラットフォーム アプリからは確実に見えないようにしてください。
ref クラスは、 const
型を含む標準 C++ の型を、 private
、 internal
、または protected private
のあらゆるメンバーの中で使用することができます。
型パラメーターを持つパブリック ref クラスは許可されません。 ユーザー定義の generic ref クラスは許可されません。 private、internal、または protected private の ref クラスは、テンプレートになることができます。
デストラクター
C++/CX では、パブリック デストラクターで delete
を呼び出すと、オブジェクトの参照カウントに関係なくデストラクターが呼び出されます。 この動作により、非 RAII リソースのカスタム クリーンアップを確定的な方法で実行できるデストラクターを定義できます。 ただし、この場合でも、オブジェクト自体はメモリから削除されません。 オブジェクトのメモリは、参照カウントがゼロに達した場合のみ解放されます。
クラスのデストラクターがパブリックでない場合は、参照カウントがゼロに達する場合にのみ、このデストラクターが呼び出されます。 プライベート デストラクターを持つオブジェクトに対して delete
を呼び出すと、コンパイラで警告 C4493 が発生し、"<型名> のデストラクターには 'public' アクセシビリティが設定されていないので、削除式の影響はありません" というメッセージが返されます。
Ref クラスのデストラクターでは、次以外の宣言はできません。
public および virtual (sealed または unsealed 型で使用できます)
protected private と non-virtual (unsealed 型でのみ使用できます)
private と non-virtual (sealed 型でのみ使用できます)
アクセシビリティ、仮想性、およびシールド性のこれ以外の組み合わせは使用できません。 デストラクターが明示的に宣言されない場合、型の基底クラスまたはメンバーに public デストラクターがあると、コンパイラは public virtual デストラクターを生成します。 それ以外の場合、コンパイラは unsealed 型には protected private non-virtual デストラクター、sealed 型には private non-virtual デストラクターを生成します。
デストラクターを既に実行しているクラスのメンバーにアクセスを試みたときの動作は不確定です。プログラムがクラッシュする可能性が高いと思われます。 public デストラクターがない型上で delete t
を呼び出しても、何の効果もありません。 型の階層構造内から既知の delete this
または private
デストラクターを持つ型または基底クラス上で protected private
を呼び出しても、何の効果もありません。
public デストラクターが宣言されると、コンパイラがコードを生成するので、ref クラスが Platform::IDisposable
を実装し、デストラクターが Dispose
メソッドを実装します。 Platform::IDisposable
は Windows::Foundation::IClosable
の C++/CX プロジェクションです。 これらのインターフェイスは、明示的に実装しないでください。
継承
Platform::Object は、すべての ref クラスの汎用基底クラスです。 ref クラスは、いずれも Platform::Object に暗黙的に変換可能で、 Object::ToStringをオーバーライドできます。 ただし、Windows ランタイム継承モデルは、一般的な継承モデルとして使用されません。つまり、C++/CX では、ユーザー定義のパブリック ref クラスは基底クラスとして使用できません。
XAML ユーザー コントロールを作成し、オブジェクトが依存関係プロパティ システムに参加している場合は、 Windows::UI::Xaml::DependencyObject
を基底クラスとして使用できます。
MyBase
から継承する unsealed クラス DependencyObject
が定義されると、コンポーネントまたはアプリ内の他の public または private の ref クラスは MyBase
から継承できるようになります。 public ref クラスの継承は、仮想メソッド、ポリモーフィック ID、およびカプセル化のオーバーライドをサポートする目的以外には実行しないでください。
private 基本 ref クラスがなくても、既存の unsealed クラスから派生できます。 独自のプログラム構造をモデル化するかまたはコードの再利用を有効にするために、オブジェクト階層構造を必要とする場合は、private または internal の ref クラスを使用するか、さらに優れた手法として標準 C++ クラスを使用してください。 public sealed ref クラス ラッパーを通じて、プライベート オブジェクト階層構造の機能を公開できます。
C++/CX に public または protected のコンストラクターを持つ ref クラスは、sealed として宣言する必要があります。 この制限があるため、C# や Visual Basic などの他の言語で記述されたクラスの場合、C++/CX で記述された Windows ランタイム コンポーネントで宣言する型から継承する方法はありません。
C++/CX で継承するための基本的な規則を次に示します。
ref クラスは多くても 1 つの基本 ref クラスからしか直接継承できませんが、任意の数のインターフェイスを実装できます。
あるクラスに public コンストラクターがある場合、さらに派生されることを防ぐために sealed として宣言する必要があります。
基底クラスが
Windows::UI::Xaml::DependencyObject
などの既存の unsealed 基底クラスから直接または間接的に派生する場合は、internal または protected private のコンストラクターを持つ public unsealed 基底クラスを作成できます。 .winmd ファイル間でのユーザー定義 ref クラスの継承はサポートされません。ただし、ref クラスは別の .winmd ファイルで定義されているインターフェイスから継承できます。 ユーザー定義の基本 ref クラスから派生クラスを作成できるのは、同じ Windows ランタイム コンポーネントまたは ユニバーサル Windows プラットフォーム アプリ内のみです。ref クラスの場合、パブリック継承のみサポートされます。
ref class C{}; public ref class D : private C //Error C3628 {};
次の例では、継承階層構造の他の ref クラスから派生する public ref クラスを公開する方法を示します。
namespace InheritanceTest2
{
namespace WFM = Windows::Foundation::Metadata;
// Base class. No public constructor.
[WFM::WebHostHidden]
public ref class Base : Windows::UI::Xaml::DependencyObject
{
internal:
Base(){}
protected:
virtual void DoSomething (){}
property Windows::UI::Xaml::DependencyProperty^ WidthProperty;
};
// Class intended for use by client code across ABI.
// Declared as sealed with public constructor.
public ref class MyPublicClass sealed : Base
{
public:
MyPublicClass(){}
//...
};
}