C++/CLI-Erweiterungen

Veröffentlicht: Dezember 2009
Von Richard Kaiser und Alexander Kaiser

Visual C++ verwendet in Windows Forms-Anwendungen eine Erweiterung von Standard-C++, die als C++/CLI bezeichnet wird. Die C++/CLI-Erweiterungen integrieren einige C#-Konzepte in C++ und ermöglichen die Nutzung der.NET Bibliothek unter C++. Im Folgenden werden einige dieser C++/CLI-Erweiterungen vorgestellt.

Dieser Artikel ist ein kurzer Auszug aus dem Buch „C++ mit Microsoft Visual C++ 2008“ (ISBN 978-3540238690), das C++ mitsamt den Visual C++-Erweiterungen (C++/CLI) umfassend darstellt. Der Verfasser dieses Buches ist ein erfahrener C++- und C#-Trainer, der auch für Firmenschulungen zur Verfügung steht.

Verweisklassen

Eine Verweisklasse wird mit

ref class* oder *ref struct

definiert und unterscheidet sich von einer nativen Klasse (die nur mit class oder struct definiert wird) vor allem dadurch, dass sie die CLI-Besonderheiten (insbesondere garbage collection) nutzen kann. So ist zum Beispiel

ref class Punkt{ // eine Verweisklasse

  int x,y;

 public:

  Punkt(int x_, int y_):x(x_),y(y_) {}

};

eine Verweisklasse, während

class Punkt_n { // eine native Klasse

  int x,y;

 public: 

  Punkt_n(int x_, int y_):x(x_),y(y_){}

};

eine native Klasse ist. Die Definition dieser Klassen unterscheidet sich nur durch das „ref“ am Anfang.

Verweisklassen gehören zu den sogenannten „verwalteten Typen“, da Objekte (Instanzen) dieser Typen auf dem Garbage Collected Heap angelegt werden und der von ihnen belegte Speicher vom Garbage Collector wieder freigegeben wird, wenn kein Verweis mehr auf das Objekt existiert. Ein solches Objekt wird über einen Zeiger angesprochen, der mit dem Symbol ^ definiert wird ­ im Unterschied zum Symbol * bei einem Zeiger auf den gewöhnlichen Heap. Das Objekt wird dann mit gcnew angelegt:

Punkt^ p=gcnew Punkt(17,18);

Ein Objekt einer nativen Klasse kann dagegen auf dem Stack oder mit new definiert werden:

Punkt_n p1(18,19);

Punkt_n* p2=new Punkt_n(19,20);

Während der Speicher für ein mit new angelegtes Objekt mit delete wieder freigegeben werden muss, ist das für ein mit gcnew angelegtes Objekt nicht notwendig. Das ist zwar möglich

delete p;

und oft auch sinnvoll. Falls das aber unterlassen wird, gibt der Garbage Collector den Speicher irgendwann einmal wieder frei, wenn kein Verweis mehr auf das Objekt existiert.

Alle Verweisklassen werden von der Basisklasse Object abgeleitet, auch ohne dass bei ihrer Definition eine Basisklasse angegeben wird. Da die Klasse Object eine virtuelle Methode

virtual String^ ToString() 

* *hat, steht diese in jeder Verweisklasse zur Verfügung:

String^ s=p->ToString();

Da diese Methode virtuell ist, kann sie überschrieben werden:

ref class Punkt {

  int x,y;

 public:

  virtual String^ ToString() override 

  { 

  return String::Format("Punkt({0},{1})",x,y); 

  }

};

Mit dem Objekt p von oben erhält man dann durch

String^ s=p->ToString(); // "Punkt(17,18)"

den als Kommentar angegebenen Text.

Alle .NET-Klassen sind Verweisklassen. Deshalb müssen alle Verweise auf Objekte dieser Klassen mit dem Symbol ^ definiert und die Objekte mit gcnew angelegt werden. So erzeugt zum Beispiel die Funktion makeTextBox eine TextBox und fügt diese mit Controls->Add dem als Argument übergebenen Formular hinzu:

TextBox^ makeTextBox(Form^ f, int x, int y, int w, int h)

{

TextBox^ t = gcnew TextBox();

t->Location = System::Drawing::Point(x, y);

t->Size = System::Drawing::Size(w, h);

t->Multiline = true;

t->Name = L"makeTextBox1";

f->Controls->Add(t);

return t;

};

Diese Funk­tion kann dann in einer Elementfunktion eines Formulars so aufgerufen werden:

TextBox^ t=makeTextBox(this,10,10,20,50);

t->AppendText("bla bla bla");

Properties

Verweisklassen unterscheiden sich von nativen Klassen auch dadurch, dass sie andere Arten von Elementen wie z.B. Properties (Eigenschaften) enthalten können. Wir haben Properties bereits bei der ersten Begegnung mit dem Eigenschaftenfenster kennengelernt und wie Va­riablen benutzt: Einer property wurde ein Wert zuge­wie­sen, und eine property wurde wie eine Variable in einem Ausdruck verwendet.

Eine property ist allerdings mehr als eine Variable: Mit einer property können Methoden und Datenelemente zum Lesen bzw. Schreiben verbun­den sein. Diese Methoden müssen bei der Definition der property angegeben werden und get und set heißen. Die Funktion get ist dann die Lesefunktion und set die Schreibfunktion. Wenn eine property

  • eine Funktion mit dem Namen get hat, muss das eine Funktion ohne Para­meter sein. Ihr Rückgabetyp muss derselbe Datentyp wie der Datentyp der Property sein. Wenn die property in einem Ausdruck verwendet (gelesen) wird, wird diese Funktion aufgerufen. Ihr Rückgabewert ist dann der Wert der property.
  • eine Funktion mit dem Namen set hat, muss das eine Funktion mit dem Rück­gabetyp void und einem einzigen Werte- oder Konstantenparameter sein, der denselben Datentyp hat wie die property. Bei einer Zuweisung an die property wird dann diese Funktion mit dem Argument aufgerufen, das zuge­wiesen wird.

Für die Eigenschaft x des Datentyps T müssen die Lese- und Schreib­methoden die folgenden Funktionstypen haben:

typedef int T;

ref class C {

  T fx;

 public: 

  property T x {

    T get()

    {

    return fx;

    }

    void set(T x_)

    {

    fx=x_*x_;

    };

  };

};

Die Klasse C kann folgendermaßen verwendet werden:

C^ c=gcnew C;

c->x=2;

int y=c->x; // y==4

Für einen Entwickler sieht eine Property wie ein „gewöhnliches Datenelement“ aus. Sie unterscheidet sich von einem solchen Datenelement aber dadurch, dass der Zugriff auf eine Property (wenn sie gelesen oder beschrieben wird) mit An­wei­sungen verbunden werden kann.

Das Konzept der Properties ist eng mit der visuellen Programmierung ver­bun­den. Da mit einer Zuweisung an eine Property Anweisungen ausgeführt werden können, lässt sich mit der Änderung einer Eigenschaft direkt die visuelle Dar­stel­lung der Komponente ändern. Wird zum Beispiel die Eigenschaft Top einer visuellen Komponente verändert, än­dert sich nicht nur der Wert des zugehörigen Datenelements, sondern außer­dem die grafische Darstellung die­ser Komponente: Sie wird an der alten Position entfernt und an der neuen Position neu gezeichnet.

Zurück: Einschränkungen bei der Kombination von nativen und verwalteten Klassen | Weiter: .NET-Klassen in C++ Windows Forms-Anwendungen