Typische C- und C++-Anweisungen

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

Eine Header-Datei, die außerhalb der Formularklasse in eine Windows Forms-Anwendung aufgenommen wird, kann im Wesentlichen alle Anweisungen von Standard-C und C++ enthalten. Auf diese Weise können auch viele bestehende C/C++-Programme mit wenig Aufwand auf .NET portiert werden. Insbesondere ist es nicht notwendig, solche Programme nach C# umzuschreiben, um sie auf .NET zu portieren.

Die folgenden Beispiele sollen einfach nur zeigen, wie das Schema aus dem letzten Abschnitt mit einfachen C/C++-Anweisungen und nicht ganz so einfachen Elementen der C++-Standardbibliothek oder der Boost-Bibliothek verwendet werden kann. Es ist nicht beabsichtigt, diese Sprachelemente zu erklären.

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.

Einige typische C/C++-Anweisungen

Die folgenden Anweisungen sind ein inhaltlich sinnloses Sammelsurium typischer C/C++ Sprachelemente, die einfach nur zeigen sollen, dass eine Windows Forms-Anwendung solche Sprachelemente enthalten kann:

const int Max=17; // eine globale Variable eines vordefinierten Typs

class Punkt { // eine Klasse

  double x,y;

 public: 

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

};

const char* Msg="Hallo Welt"; // ein nullterminierter String

#include <string.h> // eine C-Bibliothek

using namespace System::Windows::Forms;

using namespace System;

void f(TextBox^ tb, int n) // eine Funktion

{ // inhaltlich völliger Unsinn

int a[Max]; // ein Array

for (int i=0; i<Max; i++) // eine Kontrollstruktur

  a[i]=i*i;

int* p=new int; // ein Zeiger

*p=n*strlen(Msg);

for (int i=0; i<Max; i++)

  if (i%10==0)  

    tb->AppendText(String::Format("a[{0}]={1}\r\n",i,a[i]));

}

Will man den Wert der Funktion f mit einem in einer TextBox eingegebenen Parameter anzeigen, kann man eine ButtonClick-Funktion verwenden wie

private: System::Void button1_Click(System::Object^ sender, 

                                     System::EventArgs^  e) 

{

int n=Convert::ToInt32(textBox1->Text);

f(textBox1, n);

}

Wenn in diesen Beispielen nullterminierte Strings und zugehörige Funktionen wie

const char* Msg="Hallo Welt"; // ein nullterminierter String

char a[11];

strcpy(a,Msg);

verwendet werden, soll damit nur gezeigt werden, dass das möglich ist. Es soll aber nicht bedeuten, dass Sie solche Datentypen und Anweisungen verwenden sollen. Der Compiler weist durch eine Warnung wie

warning C4996: 'strcpy': This function or variable may be unsafe.

                         Consider using strcpy_s instead.

darauf hin, dass nullterminierte Strings und die zugehörigen Funktionen unsicher sein können. Allerdings ist der Hinweis, stattdessen „secure library functions“ wie strcpy_s zu verwenden, meist nur für C-Programme die richtige Alternative. In C++ besteht normalerweise keine Veranlassung, Strings mit char* darzustellen, da Stringklassen wie string oder String zur Verfügung stehen. Diese sind einfacher und mindestens genauso sicher wie die „secure library functions“.

Elemente der C++-Standardbibliothek

Eine Header-Datei, die außerhalb der Formularklasse in eine Windows Forms-Anwendung aufgenommen wird, kann auch die Klassen der C++-Standardbibliothek verwenden. Auf diese Weise steht die Standard Template Library (STL) mit ihren Stringklassen, Container-Klassen (z.B. vector) und Streamklassen in Windows Forms-Anwendungen zur Verfügung.

Die folgenden Ausführungen sollen das an einigen einfachen Beispielen zeigen.

Die Klasse string der C++-Standardbibliothek kann nach

#include <string>

using std::string; // oder: using namespace std;

verwendet werden:

string s1="Hallo Welt";

Diese Klasse hat gegenüber nullterminierten Strings viele Vorteile. Mit ihren Elementfunktionen kann man Strings bearbeiten oder nach Zeichenfolgen durchsuchen. Als einfaches Beispiel soll eine Funktion dienen, die an einen String die Zeichen „.txt“ anhängt, falls dieser nicht schon mit diesen Zeichen endet:

string appendTxtExtension(string fn)

{

if (fn.find(".txt")!=fn.length()-4)

  return fn+".txt";

else return fn;

}

Die Containerklassen der Standardbibliothek haben den Vorteil, dass sie ihren Speicher automatisch verwalten. Falls ein neues Element in einem Container abgelegt wird und der bisher reservierte Speicherplatz nicht ausreicht, wird automatisch neuer Speicher reserviert. Als einfaches Beispiel soll ein vector dienen. Diese Containerklasse steht nach

#include <vector>

using std::vector; // oder: using namespace std;

zur Verfügung. Mit

vector<int> v;

erhält man einen vector v, der int-Werte speichern kann. Neue Elemente kann man einem vector mit der Elementfunktion push_back hinzufügen. Diese Elemente kann man wie Elemente eines Arrays mit dem Indexoperator ansprechen:

void useVector(TextBox^ textBox1, int n)

{

vector<int> v;

for (int i=1; i<=n; i++) // fügt v 100 Elemente hinzu

  v.push_back(i*i); 

for (int i=0; i<v.size();i++)//oder size_t anstatt int

 textBox1->AppendText(v[i].ToString()+"\r\n");

}

Mit den fstream-Klassen kann man Dateien anlegen und lesen:

#include <fstream>

using namespace std;

void useFstream(TextBox^ textBox1, int n)

{

ofstream fout("c:\\test.dat",ios::binary);

for (int i=0; i<n; i++)

  fout.write((char*)&i,sizeof(int));

fout.close();

ifstream fin("c:\\test.dat",ios::binary);

int i;

while (fin.read((char*)&i, sizeof(int)))

  textBox1->AppendText(i.ToString()+"\r\n");

fin.close();

}

Elemente der Boost-Bibliothek

Boost ist eine Sammlung von über 50 Bibliotheken, die alle im Quelltext verfügbar sind und frei (d.h. ohne Gebühren, im Rahmen der Boost Lizenz) verwendet werden können. Sie enthalten viele Funktionen, Klassen usw., die in vielerlei Anwendungsbereichen nützlich sein können.

Das Boost Projekt geht auf eine Initiative von Mitgliedern des C++-Standardisie­rungskomitees zurück, die damit unter anderem das Ziel verfolgten, Referenz­implementationen für zukünftige C++-Erweiterungen zur Verfügung zu stellen und mit diesen praktische Erfahrun¬gen zu sammeln. Ein wichtiges Entwurfsziel ist Portabilität. Alle Bibliotheken können mit jedem weitgehend standard­konfor­men C++-Compiler unabhängig von der Plattform verwendet werden. Sie gehören zu den qualitativ hochwertigsten C++-Bibliotheken. Durch ihre große Verbreitung ist ihr Design und ihre Funktionalität auf einer breiten Basis erprobt.

Die Boost-Bibliotheken stehen im Quelltext zur Verfügung. Ihre Installation ist einfach: Nachdem man sie entpackt hat, muss man lediglich unter Projekt|Eigenschaften|Konfigurationseigenschaften|C/C++|Allgemein|Zusätzliche Includeverzeichnisse das Verzeichnis mit der Boost-Bibliothek (z.B. c:\Boost\boost_1_41_0) eintragen.

Als Beispiel soll hier die Berechnung einer CRC-Prüfsumme dienen:

#include <boost/crc.hpp>      // for boost::crc_basic, boost::crc_optimal

#include <boost/cstdint.hpp>  // for boost::uint16_t

void testCRC()

{

const char* buf="Hello World";

boost::crc_basic<16>  crc_ccitt1( 0x1021, 0xFFFF, 0, false, false );

crc_ccitt1.process_bytes( buf, strlen(buf) );

int x= crc_ccitt1.checksum() ;

}

TR1-Erweiterungen

Die sogenannten TR1-Erweiterungen sind die erste große Erweiterung der C++-Standardbibliothek seit der Verabschiedung des C++-Standards. Der Name kommt von „Technical Report“: Damit bezeichnet die Standardisierungsorganisation ISO Dokumente, die zwar keine bindenden Standards sind, aber fast das Gewicht von Standards haben, da sie durch ISO-Gremien verabschiedet wurden. Ein großer Teil der TR1-Erweiterungen wird auch Bestandteil des nächsten C++-Standards sein.

Die TR1-Erweiterungen sind ab der Version 2008 (Service Pack 1) in Visual Studio enthalten und gehören zum Namensbereich std::tr1. Als Beispiel soll hier gezeigt werden, wie man die Klasse shared_ptr verwenden kann. Diese Klasse führt eine einfache und effiziente Form von Garbage collection durch, indem sie den Speicher auf ein mit new erzeugtes Objekt wieder frei gibt, wenn kein Verweis mehr darauf existiert.

#include <memory>

using namespace std::tr1;

void testSharedPtr()

{

shared_ptr<int> s1;              // wie int* p1;

shared_ptr<int> s2(new int(17)); // wie int* p2=new int(18);

} // gibt den Speicher auf das mit new erzeugte Objekt wieder frei

Zurück: Eine Vorlage für Windows Forms-Anwendungen mit Standard-C++| Weiter: Konversionen zwischen string und String