Classi e struct di riferimento (C++/CX)
C++/CX supporta classi di riferimento definite dall'utente e struct di riferimento e classi di valori definite dall'utente e struct di valore. Queste strutture di dati sono i contenitori primari con cui C++/CX supporta il sistema di tipi Windows Runtime. Il contenuto viene emesso ai metadati in base a determinate regole specifiche e ciò consente di passare tra i componenti di Windows Runtime e le app piattaforma UWP (Universal Windows Platform) scritte in C++ o in altri linguaggi.
Le classi e gli struct di riferimento presentano le seguenti caratteristiche principali:
Devono essere dichiarati in uno spazio dei nomi, nell'ambito dello spazio dei nomi, e in tale spazio possono avere accessibilità pubblica o privata. Solo i tipi pubblici vengono emessi nei metadati. Le definizioni delle classi pubbliche annidate non sono consentite, incluse le classi enum pubbliche annidate. Per altre informazioni, vedere Spazi dei nomi e visibilità dei tipi.
Può contenere come membri C++/CX, incluse classi di riferimento, classi valore, struct di riferimento, struct valore o struct di valori nullable. Può anche contenere tipi scalari, ad
float64
esempio ,bool
e così via. Può inoltre contenere tipi C++ standard comestd::vector
o una classe personalizzata, purché non siano pubblici. I costrutti C++/CX possono averepublic
,protected
,internal
,private
oprotected private
accessibilità. Tutti i membripublic
oprotected
vengono emessi nei metadati. I tipi C++ standard devono avere accessibilitàprivate
,internal
oprotected private
, che ne impedisce l'emissione nei metadati.Possono implementare una o più classi di interfaccia o struct di interfaccia.
Possono ereditare da una classe base e le classi base a loro volta presentano ulteriori restrizioni. L'ereditarietà nelle gerarchie di classi di riferimento pubbliche ha più restrizioni rispetto all'ereditarietà nelle classi di riferimento private.
Non possono essere dichiarati come generici. Se dispongono di accessibilità privata, possono essere modelli.
La relativa durata è gestita dal conteggio dei riferimenti automatico.
Dichiarazione
Nel frammento di codice riportato di seguito viene dichiarata la classe di riferimento Person
. Si noti che il tipo C++ std::map
standard viene usato nei membri privati e l'interfaccia di Windows Runtime IMapView
viene usata nell'interfaccia pubblica. Nota inoltre che il simbolo "^" è accodato alle dichiarazioni dei tipi di riferimento.
// #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;
};
Implementazione
In questo esempio di codice viene illustrata un'implementazione della classe di riferimento Person
:
#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);
}
Utilizzo
Nell'esempio di codice riportato di seguito viene illustrato l'utilizzo della classe di riferimento Person
da parte del codice client.
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");
Puoi anche utilizzare la semantica dello stack per dichiarare una variabile della classe di riferimento locale. Un oggetto di questo tipo si comporta come una variabile basata su stack anche se la memoria viene ancora allocata in modo dinamico. Una differenza importante è che non puoi assegnare un riferimento di rilevamento (%) a una variabile dichiarata con la semantica dello stack; ciò garantisce l'azzeramento del conteggio dei riferimenti al termine della funzione. In questo esempio vengono illustrate una classe di riferimento di base Uri
e una funzione che la utilizza con la semantica dello stack:
void DoSomething()
{
Windows::Foundation::Uri docs("http://docs.microsoft.com");
Windows::Foundation::Uri^ devCenter = docs.CombineUri("/windows/");
// ...
} // both variables cleaned up here.
Gestione della memoria
Per allocare una classe di riferimento nella memoria dinamica utilizzi la parola chiave ref new
.
MyRefClass^ myClass = ref new MyRefClass();
L'operatore ^
handle-to-object è noto come hat ed è fondamentalmente un puntatore intelligente C++. La memoria a cui punta viene automaticamente distrutta quando l'ultimo handle a oggetto esce dall'ambito o viene impostato in modo esplicito su nullptr
.
Per definizione, una classe di riferimento contiene semantica di riferimento. Quando assegni una variabile della classe di riferimento, è l'handle ad essere copiato, non l'oggetto stesso. Nell'esempio riportato di seguito, dopo l'assegnazione sia myClass
che myClass2
puntano alla stessa posizione in memoria.
MyRefClass^ myClass = ref new MyRefClass();
MyRefClass^ myClass2 = myClass;
Se viene creata un'istanza di una classe di riferimento C++/CX, la relativa memoria viene inizializzata sullo zero prima di chiamare il costruttore, pertanto non è necessario inizializzare sullo zero i singoli membri, incluse le proprietà. Se la classe C++/CX deriva da una classe della libreria C++ di Windows Runtime (WRL), viene inizializzata sullo zero solo la parte della classe derivata da C++/CX.
Membri
Una classe di riferimento può contenere i membri di funzioni public
, protected
e private
; solo i membri public
e protected
vengono emessi nei metadati. Le classi annidate e le classi di riferimento sono consentite ma non possono essere public
. I campi pubblici non sono consentiti; i membri dati pubblici devono essere dichiarati come proprietà. I membri dati interni protetti o privati possono essere dei campi. Per impostazione predefinita, in una classe di riferimento l'accessibilità di tutti i membri è private
.
Uno struct di riferimento equivale a una classe di riferimento, ad eccezione del fatto che per impostazione predefinita i relativi membri hanno accessibilità public
.
Una classe di riferimento o uno public
struct di riferimento viene generata nei metadati, ma per essere utilizzabile da altre app piattaforma UWP (Universal Windows Platform) e dai componenti di Windows Runtime deve avere almeno un costruttore pubblico o protetto. Una classe di riferimento pubblica con un costruttore pubblico deve inoltre essere dichiarata come sealed
, per impedire un'ulteriore derivazione attraverso l'interfaccia applicativa binaria (ABI, Application Binary Interface).
I membri pubblici potrebbero non essere dichiarati come const
perché il sistema di tipi Windows Runtime non supporta const. Puoi usare una proprietà statica per dichiarare un membro dati pubblico con un valore costante.
Quando definisci uno struct o una classe di riferimento pubblica, tramite il compilatore vengono applicati alla classe gli attributi obbligatori e le informazioni vengono archiviate nel file con estensione winmd dell'app. Tuttavia, quando si definisce una classe di riferimento pubblica non sealed, applicare manualmente l'attributo Windows::Foundation::Metadata::WebHostHidden
per assicurarsi che la classe non sia visibile alle app piattaforma UWP (Universal Windows Platform) scritte in JavaScript.
Una classe di riferimento può avere tipi C++ standard, inclusi i tipi const
, in qualsiasi membro private
, internal
o protected private
.
Non è consentito l'utilizzo di classi di riferimento pubbliche che dispongono di parametri di tipo. Le classi di riferimento generiche definite dall'utente non sono consentite. Una classe di riferimento privata, interna o privata protetta può essere un modello.
Distruttori
In C++/CX, la chiamata delete
a un distruttore pubblico richiama il distruttore indipendentemente dal conteggio dei riferimenti dell'oggetto. Questo comportamento ti consente di definire un distruttore che esegue la pulizia personalizzata delle risorse non RAII in modo deterministico. Tuttavia, anche in questo caso, l'oggetto stesso non viene eliminato dalla memoria. La memoria per l'oggetto viene liberata solo quando il conteggio dei riferimenti raggiunge zero.
Se il distruttore di una classe non è pubblico, viene richiamato solo quando il conteggio dei riferimenti raggiunge zero. Se si chiama delete
su un oggetto con un distruttore privato, il compilatore genera l'avviso C4493, che indica che l'espressione di eliminazione non ha alcun effetto perché il distruttore di nome> di <tipo non ha accessibilità 'public'.
Un distruttore della classe di riferimento può essere dichiarato solo nei seguenti modi:
pubblico e virtuale (consentito su tipi sealed o non sealed)
privato protetto e non virtuale (consentito solo sui tipi non sealed)
privato e non virtuale (consentito solo sui tipi sealed)
Non sono consentite altre combinazioni. Se non dichiari un distruttore in modo esplicito, il compilatore genera un distruttore virtuale pubblico se qualsiasi membro o la classe base del tipo ha un distruttore pubblico. In caso contrario, il compilatore genera un distruttore non virtuale privato protetto per i tipi non sealed oppure un distruttore non virtuale privato per i tipi sealed.
Il comportamento non è definito se tenti di accedere ai membri di una classe il cui distruttore è già stato eseguito; ciò potrebbe provocare un arresto anomalo del programma. La chiamata a delete t
su un tipo senza un distruttore pubblico non ha alcun effetto. Anche la chiamata a delete this
su un tipo o una classe base con un distruttore private
o protected private
noto dalla gerarchia dei tipi non ha alcun effetto.
Quando dichiari un distruttore pubblico, il compilatore genera il codice in modo che la classe di riferimento implementi Platform::IDisposable
e il distruttore implementi il metodo Dispose
. Platform::IDisposable
è la proiezione C++/CX di Windows::Foundation::IClosable
. Non implementare mai tali interfacce in modo esplicito.
Ereditarietà
Platform::Object è la classe di base universale per tutte le classi di riferimento. Tutte le classi di riferimento sono implicitamente convertibili in Platform::Object e possono eseguire l'override di Object::ToString. Tuttavia, il modello di ereditarietà di Windows Runtime non è destinato a un modello di ereditarietà generale; in C++/CX ciò significa che una classe di riferimento pubblica definita dall'utente non può fungere da classe di base.
Se si stai creando un controllo utente XAML e l'oggetto fa parte del sistema di proprietà delle dipendenze, puoi usare Windows::UI::Xaml::DependencyObject
come classe base.
In seguito alla definizione di una classe MyBase
non sealed che eredita da DependencyObject
, altre classi di riferimento pubbliche o private nel componente o nell'app possono ereditare da MyBase
. L'ereditarietà nelle classi di riferimento pubbliche deve essere effettuata solo per supportare l'override di metodi virtuali, l'identità polimorfica e l'incapsulamento.
Non è necessario che una classe base di riferimento derivi da una classe non sealed esistente. Se è necessaria una gerarchia di oggetti per modellare la struttura del programma o consentire il riutilizzo del codice, utilizza classi di riferimento private o interne o, meglio ancora, classi C++ standard. Puoi esporre la funzionalità della gerarchia di oggetti privati tramite un wrapper di classe di riferimento sealed pubblico.
Una classe di riferimento con un costruttore pubblico o protetto in C++/CX deve essere dichiarata come sealed. Questa restrizione significa che non esiste alcun modo per le classi scritte in altri linguaggi, ad esempio C# o Visual Basic, di ereditare dai tipi dichiarati in un componente Windows Runtime scritto in C++/CX.
Ecco le regole di base per l'ereditarietà in C++/CX:
Le classi di riferimento possono ereditare direttamente solo da una classe base di riferimento, ma possono implementare un numero indefinito di interfacce.
Se una classe di riferimento dispone di un costruttore pubblico, deve essere dichiarata come sealed per impedire ulteriori derivazioni.
È possibile creare classi base non sealed pubbliche che dispongono di costruttori privati interni o protetti, a condizione che la classe base derivi in modo diretto o indiretto da una classe base non sealed esistente come
Windows::UI::Xaml::DependencyObject
. L'ereditarietà delle classi di riferimento definite dall'utente tra file con estensione winmd non è supportata; tuttavia, una classe di riferimento può ereditare da un'interfaccia definita in un altro file con estensione winmd. Puoi creare classi derivate da una classe di riferimento di base definita dall'utente solo all'interno dello stesso componente Windows Runtime o piattaforma UWP (Universal Windows Platform) app.Per le classi di riferimento, è supportata solo l'ereditarietà pubblica.
ref class C{}; public ref class D : private C //Error C3628 {};
Nell'esempio riportato di seguito viene mostrato come esporre una classe di riferimento pubblica che deriva da altre classi di riferimento in una gerarchia di ereditarietà.
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(){}
//...
};
}
Vedi anche
Sistema di tipi
Classi e struct di valore
Riferimenti al linguaggio C++/CX
Riferimenti a spazi dei nomi