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 Funktion 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 Variablen benutzt: Einer property wurde ein Wert zugewiesen, 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 verbunden 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 Parameter 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ückgabetyp 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 zugewiesen wird.
Für die Eigenschaft x des Datentyps T müssen die Lese- und Schreibmethoden 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 Anweisungen verbunden werden kann.
Das Konzept der Properties ist eng mit der visuellen Programmierung verbunden. Da mit einer Zuweisung an eine Property Anweisungen ausgeführt werden können, lässt sich mit der Änderung einer Eigenschaft direkt die visuelle Darstellung der Komponente ändern. Wird zum Beispiel die Eigenschaft Top einer visuellen Komponente verändert, ändert sich nicht nur der Wert des zugehörigen Datenelements, sondern außerdem die grafische Darstellung dieser 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