Il presente articolo è stato tradotto automaticamente.

Estensioni dei componenti

Panoramica di C++/CX

Thomas Petchel

Scarica il codice di esempio

Pronti per la creazione della prima applicazione Windows Store?O hanno già stato iscritto Windows Store apps usando HTML/JavaScript, C# o Visual Basic, e sei curioso di sapere la storia del C++?

Con le estensioni di componente di Visual C++ (C + + CX), si può prendere la tua abilità esistenti alle nuove altezze combinando codice C++ con il ricco set di controlli e librerie fornite da Windows Runtime (WinRT).E se stai usando Direct3D, si può davvero fare il vostro apps spiccano nell'archivio di Windows.

Quando alcune persone sentir parlare C + + CX che pensano di dover imparare una nuova lingua, ma il fatto è che per la stragrande maggioranza dei casi potrai avere a che fare con solo una manciata di elementi di linguaggio non standard come il ^ modificatore o le nuove parole ref.Inoltre, solo userete questi elementi al contorno della vostra applicazione, cioè, solo quando è necessario interagire con il Runtime di Windows.Il tuo portatile ISO C++ agirà ancora come il cavallo di battaglia della vostra applicazione.Forse meglio di tutti, C + + CX è 100 per cento di codice nativo.Sebbene la sua sintassi simile a C + + Common Language Infrastructure (CLI), il vostro app non portare in CLR a meno che non si desidera.

Se si dispone di codice C++ esistente che è stato già testato o semplicemente preferiscono la flessibilità e le prestazioni di C++, certi che con C + + CX non dovete imparare una nuova lingua.In questo articolo imparerete che cosa rende il C + + estensioni del linguaggio CX per la creazione di applicazioni Windows archivio unici e quando usare C + + CX per costruire il vostro app Store di Windows.

Perché scegliere C + + CX?

Ogni app ha relativo proprio insieme unico delle esigenze, proprio come ogni sviluppatore ha la propria abilità uniche e abilità.È possibile creare con successo un app Store di Windows usando C++, HTML/JavaScript o il Microsoft .NET Framework, ma qui ci sono alcuni motivi per cui si potrebbe scegliere C++:

  • Si preferisce C++ e hanno le competenze esistenti.
  • Volete approfittare del codice che hai già scritto e testato.
  • Si desidera utilizzare le librerie come Direct3D e potenziale del C++ AMP per liberare completamente l'hardware.

La risposta non deve essere uno o l'altro — si può anche mescolare e abbinare lingue.Ad esempio, quando scrisse il campione di Bing mappe viaggio Optimizer (bit.ly/13hkJhA), ho usato HTML e JavaScript per definire l'interfaccia utente e C++ per eseguire l'elaborazione in background.Il processo in background essenzialmente risolve il problema del "commesso viaggiatore".Ho usato la libreria dei modelli parallela (PPL) (bit.ly/155DPtQ) in un componente di WinRT C++ per eseguire il mio algoritmo in parallelo su tutte le CPU disponibili per migliorare le prestazioni complessive.Questo sarebbe stato difficile da fare da soli JavaScript da solo!

Come funziona il C + + CX lavoro?

Il cuore di qualsiasi Windows Store app è il Runtime di Windows.Il cuore del Runtime Windows è l'application binary interface (ABI).WinRT librerie definiscono metadati attraverso il file di metadati (.winmd) di Windows.Un file .winmd descrive i tipi pubblici che sono disponibili, e il formato assomiglia il formato che viene utilizzato negli assembly .NET Framework.In un componente di C++, il file .winmd contiene solo i metadati; il codice eseguibile si trova in un file separato.Questo è il caso per i componenti di WinRT che sono inclusi in Windows.(Per i linguaggi di .NET Framework, il file .winmd contiene il codice e i metadati, proprio come un assemblaggio di .NET Framework.) È possibile visualizzare questi metadati dal Disassembler MSIL (ILDASM) o qualsiasi lettore di metadati CLR.Figura 1 Mostra l'aspetto Windows.Foundation.winmd, che contiene molti dei tipi fondamentali di WinRT, in ILDASM.

Inspecting Windows.Foundation.winmd with ILDASM
Figura 1 ispezione Windows.Foundation.winmd con ILDASM

L'ABI è costruito utilizzando un sottoinsieme di COM per abilitare il Windows Runtime interagire con più lingue.Per poter chiamare WinRT APIs, il .NET Framework e JavaScript richiedono proiezioni che sono specifiche per ogni ambiente di lingua.Ad esempio, il tipo di stringa WinRT sottostante, HSTRING, è rappresentato come System. String in .NET, un oggetto stringa in JavaScript e la classe ref Platform::String in C + + CX.

Sebbene il C++ può interagire direttamente con COM, C + + CX mira a semplificare questo compito attraverso:

  • Riferimento automatico di conteggio.WinRT oggetti sono riferimento -­contati e tipicamente allocazione heap (non importa quale lingua li utilizza).Gli oggetti vengono distrutti quando loro conteggio di riferimento raggiunge lo zero.Il beneficio che C + + CX offre è che il conteggio dei riferimenti è automatica e uniforme.Il ^ sintassi consente sia di questi.
  • Gestione delle eccezioni.C + + CX si basa su eccezioni e non codici di errore per indicare gli errori.I valori HRESULT COM sottostanti vengono convertiti in tipi di eccezione WinRT.
  • Una sintassi facile da usare per consumare l'APIs WinRT, pur mantenendo prestazioni elevate.
  • Una facile da usare la sintassi per la creazione di nuovi tipi di WinRT.
  • Una sintassi facile da usare per la conversione di tipo, lavorando con gli eventi e altre attività.

E ricordate, anche se C + + CX prende in prestito il C + + sintassi CLI, produce puro codice nativo.Può anche interagire con il Runtime di Windows utilizzando il Windows Runtime C++ Template Library (WRL), che I'll introdurre più tardi.Tuttavia, spero che dopo l'uso di C + + CX sarete d'accordo ha senso.Ottenete le prestazioni e il controllo di codice nativo, non dovete imparare COM e il codice che interagisce con il Runtime di Windows sarà più succinto possibile — lasciandovi focalizzare sulla logica di base che rende unico il tuo app.

C + + CX è abilitato tramite l'opzione del compilatore /ZW.Questa opzione viene impostata automaticamente quando si utilizza Visual Studio per creare un progetto Windows Store.

Un gioco di Tris

Penso che il modo migliore per imparare che una nuova lingua è realmente costruire qualcosa con esso.Per illustrare le parti più comuni del C + + CX, ho scritto un app Store di Windows che gioca il tris (o a seconda di dove sei cresciuto, si potrebbe chiamare "Tris" o "Xs e Os").

Per questa applicazione, ho usato il modello vuoto App (XAML) Visual Studio .Ho chiamato il progetto TicTacToe.Questo progetto utilizza XAML per definire l'interfaccia utente dell'applicazione.Non mi concentro molto su XAML.Per saperne di più su quel lato delle cose, vedere Andy Richdell'articolo, "Introducing C + + CX e XAML" (msdn.microsoft.com/magazine/jj651573), nel numero speciale 2012 Windows 8.

Ho anche usato il modello del componente Runtime Windows per creare un componente WinRT che definisce la logica dell'applicazione.Amo il riutilizzo del codice, così ho creato un progetto di componente separato, così che chiunque può utilizzare la logica di gioco principale in qualsiasi app Store Windows utilizzando XAML e c#, Visual Basico C++.

Figura 2 Mostra l'aspetto dell'app.

The TicTacToe Ap
Figura 2 l'applicazione di TicTacToe

Quando ho lavorato sul progetto C++ Hilo (bit.ly/Wy5E92), sono caduto nell'amore con il pattern Model-View-ViewModel (MVVM).MVVM è un modello architettonico che consente di separare l'aspetto, o vista, della vostra applicazione, dai suoi dati sottostanti, o modello.Il modello di visualizzazione si connette la vista per il modello.Anche se non ho usato full-on MVVM per il mio gioco tris, ho trovato che usando dati vincolante per separare l'interfaccia utente dalla logica di app fatto l'app più facile da scrivere, più leggibile e più facile da mantenere in futuro.Per ulteriori informazioni su come abbiamo usato MVVM nel progetto Hilo C++, vedere bit.ly/XxigPg.

Per collegare l'app per il componente di WinRT, ho aggiunto un riferimento al progetto TicTacToeLibrary nella finestra di dialogo Pagine delle proprietà del progetto TicTacToe.

Impostando semplicemente il riferimento, il progetto TicTacToe ha accesso a tutti i pubblici C + + tipi CX nel progetto TicTacToeLibrary.Non è necessario specificare qualsiasi # includere direttive o fare qualsiasi altra cosa.

Creazione dell'interfaccia utente di TicTacToe

Come detto in precedenza, non si va molto in XAML, ma nel mio layout verticale, ho creato un'area per visualizzare il punteggio, uno per la principale area giochi e uno per impostare la prossima partita (si può vedere il codice XAML nel file MainPage. xaml nel codice accompagnamento Scarica).Ancora una volta, ho usato abbastanza estesamente qui associazione dati.

La definizione della classe MainPage (MainPage.h) è mostrata Figura 3.

Figura 3 la definizione della classe MainPage

#pragma once
#include "MainPage.g.h"
namespace TicTacToe
{
  public ref class MainPage sealed
  {
  public:
    MainPage();
    property TicTacToeLibrary::GameProcessor^ Processor
    {
      TicTacToeLibrary::GameProcessor^ get() { return m_processor; }
    }
  protected:
    virtual void OnNavigatedTo(      
        Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override;
  private:
    TicTacToeLibrary::GameProcessor^ m_processor;
  };
}

Così che cosa è in MainPage.g.h? A. abuso file contiene un compilatore -­generati definizione di classe parziale per pagine XAML. Fondamentalmente, queste definizioni parziali definiscono le classi di base richieste e variabili membro per qualsiasi elemento XAML che dispone dell'attributo X:Name. Qui è MainPage.g.h:

namespace TicTacToe
{
  partial ref class MainPage :
    public ::Windows::UI::Xaml::Controls::Page,
    public ::Windows::UI::Xaml::Markup::IComponentConnector
  {
  public:
    void InitializeComponent();
    virtual void Connect(int connectionId, ::Platform::Object^ target);
  private:
    bool _contentLoaded;
  };
}

La parola chiave partial è importante perché consente una dichiarazione del tipo di file di calibrazione. In questo caso, MainPage.g.h contiene parti generato dal compilatore, e MainPage.h contiene le parti aggiuntive che è possibile definire.

Notare le parole di classe pubblico e ref nella dichiarazione MainPage. Una differenza tra C + + CX e C++ è il concetto di accessibilità di classe. Se sei un programmatore .NET, sarete familiarità con questo. Accessibilità di classe significa che se un tipo o un metodo è visibile nei metadati e quindi accessibile da componenti esterni. A + C + CX tipo può essere pubblici o privati. Pubblico significa che la classe MainPage può accedere di fuori del modulo (ad esempio, dal Runtime di Windows o da un altro componente di WinRT). Un tipo privato accessibile solo all'interno del modulo. Tipi privati dare più libertà di utilizzare tipi C++ in metodi pubblici, che non è possibile con i tipi pubblici. In questo caso, classe MainPage è pubblica in modo che sia accessibile a XAML. Potrai guardare alcuni esempi di tipi privati più tardi.

Le parole chiave classe ref indicare al compilatore che questo è un tipo di WinRT e non un tipo di C++. Una classe di riferimento è allocata sull'heap e tutta la sua vita è contato di riferimento. Perché tipi ref sono contati di riferimento, loro vite sono deterministiche. Quando viene rilasciato l'ultimo riferimento a un oggetto di tipo ref, il distruttore viene chiamato e viene rilasciata la memoria per quell'oggetto. Confronta questo per .NET, dove le vite sono meno deterministiche e garbage collection viene utilizzato per liberare memoria.

Quando si crea un'istanza di un tipo di riferimento, si utilizza in genere la ^ modificatore (pronunciato "cappello"). Il ^ modificatore è simile a un puntatore di C++ (*), ma dice al compilatore di inserire il codice per gestire il conteggio dei riferimenti dell'oggetto automaticamente ed elimina l'oggetto quando il conteggio si azzera.

Per creare una struttura di dati (POD) vecchia pianura, utilizzare una classe di valore o di valore struct. Tipi di valore hanno una dimensione fissa e consistono solo campi. A differenza dei tipi ref, essi non hanno proprietà. Windows::Foundation::DateTime e Windows::Foundation::Rect sono due esempi di tipi di valore WinRT. Quando si crea un'istanza di tipi di valore, non si usa il ^ modificatore:

Windows::Foundation::Rect bounds(0, 0, 100, 100);

Inoltre si noti che MainPage è dichiarato come sigillato. La parola chiave sigillata, che è simile al C + + 11 parola chiave finale, impedisce l'ulteriore derivazione di quel tipo. MainPage è chiuso perché qualsiasi tipo public ref che dispone di un costruttore pubblico deve anche essere dichiarata come sealed. Questo è perché il runtime è indipendente dal linguaggio e non tutte le lingue (ad esempio, JavaScript) capiscono l'ereditarietà.

Ora dirigere la sua attenzione ai membri MainPage. La variabile membro m_processor (la classe GameProcessor è definita nel progetto componente WinRT — parlerò di quel tipo in seguito) è privato, semplicemente perché la classe MainPage è sigillata e non non c'è alcuna possibilità che una classe derivata può usare (e, in generale, i membri dati dovrebbero essere privati quando è possibile applicare l'incapsulamento). Il metodo OnNavigatedTo è protetta perché la classe Windows::UI::Xaml::Controls::Page, da cui deriva il MainPage, dichiara questo metodo come protetto. Il costruttore e la proprietà processore deve essere raggiunta da XAML, e quindi entrambi sono pubblici.

Hai già familiarità con gli specificatori di accesso public, protected e private; loro significati in C + + CX sono le stesse come in C++. Imparare circa interni e altri C + + vedere identificatori di CX, bit.ly/Xqb5Xe. Vedrete un esempio di interni più tardi.

Una classe di riferimento possa avere tipi solo pubblicamente accessibili nelle sue sezioni public e protected — cioè, tipi solo primitivi, public ref o tipi di valore pubblico. Al contrario, un tipo C++ può contenere tipi ref come variabili membro, nelle firme del metodo e nelle variabili locali della funzione. Ecco un esempio dal progetto Hilo C++:

std::vector<Windows::Storage::IStorageItem^> m_createdFiles;

Il team di Hilo utilizza std:: Vector e non Platform::Collections::Vector per questa variabile membro privata perché noi non esporre la collezione di fuori della classe. Usando std:: Vector ci aiuta a utilizzare C++ codice quanto più possibile e rende l'intenzione di cancellare.

Passando al costruttore MainPage:

MainPage::MainPage() : m_processor(ref 
  new TicTacToeLibrary::GameProcessor())
{
  InitializeComponent();
  DataContext = m_processor;
}

Io uso le nuove parole ref per creare un'istanza dell'oggetto GameProcessor. Utilizzare ref nuovo invece di nuovo per costruire oggetti di tipo riferimento WinRT. Quando si stanno creando oggetti nelle funzioni, è possibile utilizzare la parola chiave auto C++ per ridurre la necessità di specificare il nome del tipo o l'uso di ^:

auto processor = ref new TicTacToeLibrary::GameProcessor();

Creazione di una libreria di TicTacToe

Il codice della libreria per il gioco TicTacToe contiene una miscela di C++ e di C + + CX. Per questa applicazione, ho finto che avevo qualche codice C++ esistente che avevo già scritto e testato. Inserito questo codice direttamente, aggiunta di C + + codice CX solo per collegare l'implementazione interna di XAML. In altre parole, ho usato C + + CX solo per colmare i due mondi insieme. Andiamo a piedi attraverso alcune parti importanti della biblioteca ed evidenziare eventuali C + + caratteristiche CX non già discussi.

La classe GameProcessor serve come il contesto dati per l'interfaccia utente (modello vista pensate se si ha familiarità con MVVM). Ho usato due attributi, BindableAttribute e WebHostHiddenAttribute, quando si dichiara questa classe (come .NET, è possibile omettere la parte di "Attributo" quando si dichiarano gli attributi):

[Windows::UI::Xaml::Data::Bindable]
[Windows::Foundation::Metadata::WebHostHidden]
public ref class GameProcessor sealed : public Common::BindableBase

Il BindableAttribute produce i metadati che indica al Runtime di Windows che il tipo supporta l'associazione dati. Questo assicura che tutte le proprietà pubbliche del tipo sono visibili ai componenti di XAML. Derivano da BindableBase per implementare la funzionalità necessaria per rendere vincolante il lavoro. Perché BindableBase è destinato per l'uso di XAML e non JavaScript, utilizza il WebHost­HiddenAttribute (bit.ly/ZsAOV3) attributo. Per convenzione, ho segnato anche la classe GameProcessor con questo attributo essenzialmente nasconderlo da JavaScript.

Ho separato la proprietà di GameProcessor in sezioni interne e pubbliche. Le proprietà pubbliche sono esposti a XAML; le proprietà interne sono esposti solo per altri tipi e funzioni nella libreria. Ho sentito che questa distinzione aiuta a rendere rendere più evidente l'intento del codice.

Un modello di utilizzo proprietà comune è vincolante raccolte in XAML:

property Windows::Foundation::Collections::IObservableVector<Cell^>^ Cells
{
  Windows::Foundation::Collections::IObservableVector<Cell^>^ get()
    { return m_cells; }
}

Questa proprietà definisce i dati del modello per le cellule che appaiono sulla griglia di partenza. Quando cambia il valore delle celle, il codice XAML viene aggiornato automaticamente. Il tipo della proprietà è IObservableVector, che è uno dei diversi tipi definiti in modo specifico per C + + CX per consentire la completa interoperabilità con il Runtime di Windows. Il Runtime di Windows definisce le interfacce insieme indipendente dal linguaggio, e ogni linguaggio implementa le interfacce a modo suo. In C + + CX, lo spazio dei nomi Platform::Collections fornisce tipi come vettore e la mappa che forniscono implementazioni concrete di queste interfacce di collezioni. Pertanto, posso dichiarare la proprietà di cellule come IObservableVector ma indietro che la proprietà di un oggetto vettoriale, che è specifico per C + + CX:

Platform::Collections::Vector<Cell^>^ m_cells;

Così quando utilizzi le collezioni Platform::String e Platform::Collections contro i tipi standard e le raccolte? Ad esempio, si utilizzi std:: Vector o Platform::Collections::Vector per memorizzare i vostri dati? Come regola generale, utilizzare funzionalità piattaforma quando ho intenzione di lavorare principalmente con il Runtime di Windows e tipi standard come std:: wstring e std:: Vector per mio codice interno o computazionalmente intensivi. È possibile convertire facilmente anche tra vettore e std:: Vector quando è necessario. È possibile creare un vettore da un std:: Vector o è possibile utilizzare to_vector per creare un std:: Vector da un vettore:

std::vector<int> more_numbers =
  Windows::Foundation::Collections::to_vector(result);

C'è un costo copia associato durante il marshalling tra i tipi di due vettori, così ancora una volta, considerare quale tipo è appropriato nel codice.

Un altro compito comune è la conversione tra std:: wstring e Platform::String. Ecco come:

// Convert std::wstring to Platform::String.
std::wstring s1(L"Hello");
auto s2 = ref new Platform::String(s1.c_str());
// Convert back from Platform::String to std::wstring.
// String::Data returns a C-style string, so you don’t need
// to create a std::wstring if you don’t need it.
std::wstring s3(s2->Data());
// Here's another way to convert back.
std::wstring s4(begin(s2), end(s2));

Ci sono due punti interessanti da notare nell'implementazione della classe di GameProcessor (GameProcessor.cpp). Utilizzare innanzitutto solo C++ standard per implementare la funzione checkEndOfGame. Questo è un posto dove ho voluto illustrare come incorporare codice C++ esistente che avevo già scritto e testato.

Il secondo punto è l'utilizzo della programmazione asincrona. Quando è il momento di passare il turno, utilizzare la classe task PPL per elaborare i giocatori del computer in background, come mostrato Figura 4.

Figura 4 utilizzando il PPL Task classe per i giocatori di Computer di processo in Background

void GameProcessor::SwitchPlayers()
{
  // Switch player by toggling pointer.
m_currentPlayer = (m_currentPlayer == m_player1) ?
m_player2 : m_player1;
  // If the current player is computer-controlled, call the ThinkAsync
  // method in the background, and then process the computer's move.
if (m_currentPlayer->Player == TicTacToeLibrary::PlayerType::Computer)
  {
    m_currentThinkOp =
      m_currentPlayer->ThinkAsync(ref new Vector<wchar_t>(m_gameBoard));
    m_currentThinkOp->Progress =
      ref new AsyncOperationProgressHandler<uint32, double>([this](
      IAsyncOperationWithProgress<uint32, double>^ asyncInfo, double value)
      {
        (void) asyncInfo; // Unused parameter
        // Update progress bar.
m_backgroundProgress = value;
        OnPropertyChanged("BackgroundProgress");
      });
      // Create a task that wraps the async operation.
After the task
      // completes, select the cell that the computer chose.
create_task(m_currentThinkOp).then([this](task<uint32> previousTask)
      {
        m_currentThinkOp = nullptr;
        // I use a task-based continuation here to ensure this continuation
        // runs to guarantee the UI is updated.
You should consider putting
        // a try/catch block around calls to task::get to handle any errors.
uint32 move = previousTask.get();
        // Choose the cell.
m_cells->GetAt(move)->Select(nullptr);
        // Reset the progress bar.
m_backgroundProgress = 0.0;
        OnPropertyChanged("BackgroundProgress");
      }, task_continuation_context::use_current());
  }
}

Se sei un programmatore .NET, pensare di attività e il relativo metodo poi come la versione C++ di async e attendono in c#. Attività sono disponibili da qualsiasi programma C++, ma li userete per tutto il C + + codice CX per mantenere il vostro app Store di Windows veloce e fluido. Per ulteriori informazioni sulla programmazione asincrona in apps Store Windows, leggere l'articolo di febbraio 2012 di Artur Laksberg, "Asynchronous Programming in C++ utilizzando PPL" (msdn.microsoft.com/magazine/hh781020) e l'articolo di MSDN Library al msdn.microsoft.com/library/hh750082.

La classe di cella modelli una cella sul tabellone. Due cose nuove che illustra questa classe sono eventi e riferimenti deboli.

La griglia per l'area di gioco TicTacToe consiste di Windows::UI::Xaml::Controls::Button controlli. Un controllo Button genera un evento Click, ma può anche rispondere all'input dell'utente definendo un oggetto ICommand che definisce il contratto per comandare. Utilizzare l'interfaccia ICommand anziché l'evento Click che oggetti cella possono rispondere direttamente. In XAML per i pulsanti che definiscono le cellule, la proprietà Command si lega alla proprietà Cell::SelectCommand:

<Button Width="133" Height="133" Command="{Binding SelectCommand}"
  Content="{Binding Text}" Foreground="{Binding ForegroundBrush}"
  BorderThickness="2" BorderBrush="White" FontSize="72"/>

Ho usato la classe DelegateCommand Hilo per implementare l'interfaccia ICommand. DelegateCommand detiene la funzione da chiamare quando viene emesso il comando e una funzione opzionale che determina se il comando può essere emesso. Ecco come impostare il comando per ogni cella:

m_selectCommand = ref new DelegateCommand(
  ref new ExecuteDelegate(this, &Cell::Select), nullptr);

Comunemente userete eventi predefiniti quando facendo XAML di programmazione, ma è anche possibile definire i propri eventi. Ho creato un evento che viene generato quando si seleziona un oggetto Cell. La classe GameProcessor gestisce questo evento controllando se il gioco è finito e il giocatore corrente di commutazione, se necessario.

Per creare un evento, è necessario innanzitutto creare un tipo delegato. Pensare a un tipo delegato come un puntatore a funzione o un oggetto di funzione:

delegate void CellSelectedHandler(Cell^ sender);

Poi a creare un evento per ogni oggetto Cell:

event CellSelectedHandler^ CellSelected;

Ecco come la classe GameProcessor sottoscrive l'evento per ogni cella:

for (auto cell : m_cells)
{
  cell->CellSelected += ref new CellSelectedHandler(
    this, &GameProcessor::CellSelected);
}

Un delegato che è costruito da un ^ e una funzione di puntatore a membro (PMF) contiene solo un riferimento debole per la ^ oggetto, quindi questo costrutto non causerà riferimenti circolari.

Ecco come oggetti cella generano l'evento quando essi sono selezionati:

void Cell::Select(Platform::Object^ parameter)
{
  (void)parameter;
  auto gameProcessor = 
    m_gameProcessor.Resolve<GameProcessor>();
  if (m_mark == L'\0' && gameProcessor != nullptr &&
    !gameProcessor->IsThinking && 
    !gameProcessor->CanCreateNewGame)
  {
    m_mark = gameProcessor->CurrentPlayer->Symbol;
    OnPropertyChanged("Text");
    CellSelected(this);
  }
}

Qual è lo scopo della chiamata Resolve nel codice precedente? Beh, la classe GameProcessor contiene un insieme di oggetti cella, ma voglio che ciascun oggetto Cell per poter accedere a suo padre GameProcessor. Se la cella ha tenuto un forte riferimento a suo padre — in altre parole, un GameProcessor ^ — sarebbe creare un riferimento circolare. Riferimenti circolari possono causare oggetti che mai sarà liberato perché l'associazione reciproca fa sì che entrambi gli oggetti di avere sempre almeno un riferimento. Per evitare questo, creo una variabile membro Platform::WeakReference e impostarlo dal Costruttore Cell (pensare molto attentamente la gestione della durata e che cosa che cosa possiedono oggetti!):

Platform::WeakReference m_gameProcessor;

Quando mi chiamano WeakReference::Resolve, nullptr viene restituito se l'oggetto non esiste più. Perché GameProcessor possiede oggetti cella, mi aspetto che l'oggetto GameProcessor per essere sempre valida.

Nel caso del mio gioco TicTacToe, posso rompere il riferimento circolare ogni volta che viene creato un nuovo gioco da tavolo, ma in generale, cerco di evitare la necessità di rompere i riferimenti circolari, perché può rendere codice meno gestibile. Pertanto, quando ho un rapporto padre-figlio e i bambini hanno bisogno di accedere ai loro genitori, utilizzare riferimenti deboli.

Lavorando con le interfacce

Per distinguere tra giocatori umani e computer, ho creato un'interfaccia IPlayer con implementazioni concrete HumanPlayer e ComputerPlayer. La classe GameProcessor contiene due oggetti IPlayer — uno per ogni giocatore e un ulteriore riferimento al giocatore corrente:

 

IPlayer^ m_player1;
IPlayer^ m_player2;
IPlayer^ m_currentPlayer;

Figura 5 Mostra l'interfaccia di IPlayer.

Figura 5 l'interfaccia IPlayer

private interface class IPlayer
{
  property PlayerType Player
  {
    PlayerType get();
  }
  property wchar_t Symbol
  {
    wchar_t get();
  }
  virtual Windows::Foundation::IAsyncOperationWithProgress<uint32, double>^
    ThinkAsync(Windows::Foundation::Collections::IVector<wchar_t>^ gameBoard);
};

Perché l'interfaccia IPlayer è privata, perché non basta utilizzare classi C++? Per essere onesti, l'ho fatto per mostrare come creare un'interfaccia e come creare un tipo privato che non è pubblicato ai metadati. Se io fossi la creazione di una libreria riutilizzabile, potrei dichiaro IPlayer come un'interfaccia pubblica così altre applicazioni potrebbero usarlo.  In caso contrario, potrei scegliere di attaccare con C++ e non utilizzare un C + + interfaccia CX.

La classe ComputerPlayer implementa ThinkAsync eseguendo l'algoritmo minimax in background (vedere il file ComputerPlayer.cpp nel download del codice per esplorare questa implementazione).

Minimax è un algoritmo comune durante la creazione di componenti di intelligenza artificiale per giochi come tris. Potete saperne di più su minimax nel libro "intelligenza artificiale: Un approccio moderno"(Prentice Hall, 2010), di Stuart Russell e Peter Norvig.

Ho adattato Russel e di Norvig algoritmo minimax da eseguire in parallelo utilizzando il PPL (vedere minimax.h nel download del codice). Questa è stata una grande opportunità per utilizzare puro C + + 11 per scrivere il processore -­parte intensiva della mia app. Ho ancora a battere il computer e non hanno mai visto il computer battere se stesso in un gioco di computer contro computer. Ammetto che questo non fa per il gioco più eccitante, così qui è il tuo invito all'azione: Aggiungere logica aggiuntiva per far vincere il gioco. Un modo basilare per fare questo sarebbe avere il computer rendere casuale selezioni a caso volte. Un modo più sofisticato sarebbe avere il computer volutamente sceglie a caso una mossa ottima volte. Per i punti bonus, aggiungere un controllo slider dell'interfaccia utente che regola la difficoltà del gioco (meno difficili, più computer o sceglie una mossa ottima o almeno uno casuale).

Per la classe HumanPlayer, ThinkAsync ha nulla a che fare, così mi tiro Platform::NotImplementedException. Questo richiede che verifico la proprietà di IPlayer::Player prima, ma mi fa risparmiare un compito:

IAsyncOperationWithProgress<uint32, double>^
  HumanPlayer::ThinkAsync(IVector<wchar_t>^ gameBoard)
{
  (void) gameBoard;
  throw ref new NotImplementedException();
}

Il WRL

C'è una grande mazza disponibili nella casella degli strumenti per quando C + + CX non fa che cosa avete bisogno o quando si preferisce lavorare direttamente con COM: il WRL. Ad esempio, quando si crea un'estensione media di Microsoft Media Foundation, è necessario creare un componente che implementa le interfacce COM sia WinRT. Perché C + + CX ref classi solo possono implementare le interfacce WinRT, creare un'estensione media è necessario utilizzare il WRL perché supporta l'implementazione di interfacce COM sia WinRT. Per ulteriori informazioni sulla programmazione WRL, vedere bit.ly/YE8Dxu.

Andando più in profondità

In un primo momento ho avuto perplessità circa C + + estensioni CX, ma presto divenne secondo natura, e li gradisco perché essi mi permettono di scrivere rapidamente applicazioni Windows Store e utilizzare moderni idiomi di C++. Se sei uno sviluppatore C++, mi raccomando che almeno dare loro un colpo.

Ho rivisto solo alcuni dei modelli comuni che incontrerai durante la scrittura di C + + codice CX. Hilo, un app foto usando C++ e XAML, va più in profondità ed è molto più completo. Ho avuto un grande tempo lavorando sul progetto C++ Hilo, e mi riferisco in realtà torna ad esso spesso come scrivere nuove applicazioni. Consigliamo di verificarlo presso bit.ly/15xZ5JL.

Thomas Petchel funziona come un anziano scrittore programmazione in Microsoft Developer Division. Ha trascorso gli ultimi otto anni con il team di Visual Studio creazione di documentazione ed esempi di codice per il pubblico di sviluppatore.

Grazie ai seguenti esperti tecnici per la revisione di questo articolo: Michael Blome (Microsoft) e James McNellis (Microsoft)
Michael Blome ha lavorato per più di 10 anni presso Microsoft, impegnata nell'attività di scrittura e riscrittura documentazione MSDN per Visual C++, DirectShow, c# language reference, LINQ e programmazione in ambito .NET parallela Sisifo.

James McNellis è un aficionado di C++ e sviluppatori di software del team di Visual C++ di Microsoft, dove egli costruisce impressionante librerie C e C++. È un prolifico collaboratore su Stack Overflow, tweets a @JamesMcNellis e può essere trovato altrove via jamesmcnellis.com.