Beispiel für die sinnvolle Nutzung von Konstanten
Habe ich irgendwo eine Zahl
int i = 123;
und setze eine Referenz darauf, die aber den Typ double haben soll(warum auch immer),
dann bekomme ich mit
double &ref1 = i;
einen Typkonvertierungsfehler, mit
const double &ref1 = i;
jedoch nicht !!
Außerdem sind weder Arrays von Referenzen, Zeiger auf Referenzen, noch Referenzen
auf Referenzen zulässig. Zeiger auf Zeiger sind (außer im Kopf) kein Problem.
Zeiger auf Funktionen
In C++ ist der Name einer Funktion ein konstanter Zeiger auf die Funktion. Er addressiert den Maschinencode
der Funktion. Bsp.:
Prototyp einer Funktion
bool compare(double, double);
Zeiger auf die obige Funktion
bool (*ZFkt)(double, double);
ZFkt = compare;
//Der soeben erschaffene Zeiger erhält die Funktion nun als Zuweisung
Beim Aufruf ist es nun egal, ob man den zeiger mit oder ohne Stern schreibt:
if((*ZFkt)(9.1, 6.3)){ Tue dies und scheue niemand } oder auch
if(ZFkt(9.1, 6.3)){ Tue dies und scheue niemand }
Persistenz - Speichern von Dateien auf Festplatte
Um ein Programm nicht nur durch den RAM - Speicher zu jagen, sondern die Daten zu speichern,
um sie auch später weiter verwenden zu können, muss man die Daten, mit denen das Programm arbeitet,
wie z.B. eingegebene Werte persistent machen, was soviel heißt, wie sie auf der Festplatte abzuspeichern.
Dazu inkludiert man die Klasse fstream und verwendet deren Methoden open, close, write, read und
den wandernden Zeiger seek.
Wir haben z.B. in einer Textdatei test1.txt folgenden String: "A111B222C333" und wollen auf test1.txt
lesend und schreibend zugreifen. Das geht mit dem folgenden Programm :
#include<iostream>
#include<fstream>
#include<string>
using namespace std;
void main( )
{
fstream testdatei;
char ch[20] = {"vvvvvvvvvvvvvvvvvvv"};
ios : : openmode m1 = ios : : in | ios : : out;
testdatei.open("C:\\Dokumente und Einstellungen\\user07\\Desktop\\test1.txt", m1);
testdatei.seekp(0L);
testdatei.write("XYZ",3);
testdatei.seekg(0L); //auf Anfang der Datei setzen, das ist die 0, L steht für Long
testdatei.read(ch, 4);
cout<<ch<<endl;
testdatei.seekg(6L);
testdatei.read(ch, 3);
cout<<ch<<endl;
testdatei.close( );
}
In fstream befinden sich die Definitionen für Eingabe und Ausgabe - Klassen. seek, der wandernde
Zeiger sucht nach einem bestimmten Byte im Bytestream auf der Festplatte, anders als z.B. die Methode
find in der Klasse string oder in Container - Klassen, die ja nach bestimmten alfabetischen
oder numerischen Attributen sucht. seek zeigt auf Methoden. So zeigt seekg auf eine get - Methode
und seekp auf eine put - Methode. Da seek ja auf Methoden zeigt, gibt man ihm Parameter mit,
z.B. 0L, wobei die 0 für den Anfang der Datei steht und L für den Datentyp Long, mit dem seek intern arbeitet.
Um mit dem Bytestream arbeiten zu können, braucht man noch ein Objekt der Klasse ios vom Datentyp
ios : : openmode. Wir nennen das Objekt m1. Initialisiert wird es mit ios : : in bitweises Oder ios : : out.
Nun können wir aus unserer Testdatei lesen und in sie schreiben.
Möglichkeiten des Makropräprozessors
#include
#define SCHREIB cout
#define MEINEN <<
#define NAMEN "Klaus"
#define IM <<endl
#define FENSTER ;
#define HAUPTPROGRAMM void main( )
using namespace std;
HAUPTPROGRAMM
{
SCHREIB MEINEN NAMEN IM FENSTER
}
Das läuft !!!
Erstellen von Win32 - Applikationen
Traditionell steht am Anfang eines Windows - Programmierkurses ein "Hello World" - Programm,
das als zunächst leere Win32 Anwendung erzeugt wird.
Ganz oben steht
#include<windows.h>
windows.h ist eine Header - Datei, die innendrin ziemlich komplex ist und daher erstmal noch
nicht besonders gut lesbar. Verwendet man die Header - Datei in einem DOS - Programm, so wird man
die lustigsten Fehlermeldungen zu sehen bekommen.
Sie enthält eine Vielzahl von Definitionen solcher Begriffe, die in Windows - Programmen
dann als Schlüsselwörter verwendet werden. Dabei handelt es sich um Funktionen,
die global verwendet werden, Konstanten, die meistens den Typ unsigned int haben, Strukturen,
die hier vermutlich aus Gründen des historischen Gewachsenseins an Stelle von Klassen verwendet werden
und eigene Typdefinitionen, z.B. UINT für unsigned Integer.
Von einer Struktur können Objekte wie von einer Klasse gebildet werden, die dann jeweils alle
Elemente der Struktur enthalten. Die Elemente sind per default public.
Standardmäßig implementiert ein Windows - Programm zwei Funktionen :
WinMain( ) //entspricht unserer bisherigen main - Funktion und
WndProc( ) //die Windows - Procedure
proprietäre Windows Datentypen
Die speziell für Windows definierten Datentypen werden komplett in Großbuchstaben geschrieben.
BOOL bool
BSTR ein 32 Bit char Zeiger
BYTE ein 8 Bit int ohne Vorzeichen
COLORREF ein 32 Bit Wert als Farbwert
DWORD ein 32 Bit unsigned int oder die Addresse eines Segments und seines assoziierten Offsets
LONG ein 32 Bit int mit Vorzeichen, long int
LPARAM ein 32 Bit Wert, der als Parameter einer Windows Prozedur oder CALLBACK Funktion
übergeben wird, WndProc ist eine CALLBACK Funktion.
LPCSTR ein 32 Bit Zeiger auf einen String aus const char
LPSTR ein 32 Bit Zeiger auf einen char String
LPCTSTR ein 32 Bit Zeiger auf einen String aus const char, der für Unicode und DBCS
verwendet werden kann.
LPTSTR ein 32 Bit Zeiger auf einen String aus char, der für Unicode und DBCS
verwendet werden kann.
LPVOID ein 32 Bit Zeiger auf einen unspezifizierten Datentyp
LRESULT ein 32 Bit Wert, der von einer Windows Prozedur(WndProc) oder CALLBACK
Funktion zurückgegeben wird.
UINT Unsigned int, 16 Bit auf 16 Bit Windows, 32 Bit auf 32 Bit Windows
WNDPROC 32 Bit Zeiger auf eine Windows Prozedur
WORD 16 Bit unsigned int
WPARAM als Parameter an eine window procedure oder CALLBACK Fkt übergebener Wert,
16 Bit auf 16 Bit Windows, 32 Bit auf 32 Bit Windows
Die beiden folgenden Datentypen gibt es dann nur in den MFC :
POSITION MFC collection classes : ein Wert, der die Position eines Elements in einer
collection angibt.
LPCRECT 32 Bit Zeiger auf eine konstante(das heißt hier wirklich "unveränderliche")
Rechteckstruktur(RECT)
Alternativ können die in C++ bekannten Standarddatentypen weiterhin verwendet werden.
Obige Erklärungen, wie DWORD, die jetzt noch ein bisschen "neblig" erscheinen, werden später
klarer werden.
Die WinMain - Funktion
In eine Variable vom Struktur - Typ WNDCLASSEX werden in das Datenelement style, Schreibweise :
wndclassex.style(oder hund.style) die "CS_"(Class Style) Konstanten CS_HREDRAW und CS_VREDRAW
für horizontales bzw. vertikales Neuzeichnen eingelesen. Sie sind durch bitweises Oder
miteinander verknüpft. Wenn man auch Doppelklicks auswerten will, muss man hier - ebenfalls
verknüpft durch bitweises Oder CS_DBLCLKS angeben.
Alle weiteren Angaben in diesem Block, die durch einen Punkt getrennt hinter dem Struct Variablennamen
stehen, beziehen sich auf Art und Weise der Fensterzeichnung. So können dem Datenelement
wndclass.hbrBackground über die Funktion GetStockObject verschiedene Hintergrundfarben für das
jeweils zu erstellende Fenster übergeben werden, Bsp.:
wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wndclass.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);
wndclass.hbrBackground = (HBRUSH) GetStockObject(DKGRAY_BRUSH);
wndclass.hbrBackground = (HBRUSH) GetStockObject(LTGRAY_BRUSH);
HBRUSH ist ein typecast, der an dieser Stelle vorgenommen wird(erzwungener Datentyp).
Wie man sieht, ist die Auswahl an Farben leider etwas eingeschränkt.
Die Funktion CreateWindow erhält Parameter und übergibt diese einem Objekt der Struktur HWND.
Hierzu gehören die Fensterüberschrift, "window caption", der Fensterstil, "window style", die x und y
Startpositionen für das Fenster, Länge der x und y Achse und noch Einiges mehr.
Ein Objekt des Datentyps HWND ist ein sogenannter "Windows Handler", ein "Fensterhantierer" also.
Die Windows - Nachrichtenschleife
Die Windows Nachrichtenschleife befindet sich in der WinMain - Funktion. Sie erhält Nachrichten vom
Typ MSG, die ich in dem Beispielprogramm, das man unten aufrufen kann, "vogel" genannt habe.
Installiert man in der Nachrichtenschleife einen Zähler, so wird dieser sehr schnell hochgesetzt,
weil alle Windows - Nachrichten hier verarbeitet werden. Beispiele für Nachrichten sind :
WM_PAINT, WM_DESTROY, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK usw.
Die Nachrichten werden in einer Schleife, while (GetMessage (&vogel, NULL, 0, 0)) entgegengenommen,
wobei "vogel" eine Variable vom Typ MSG ist.
Die Funktion WndProc erhält u.A. als Parameter eine Variable vom Typ UINT unter einem frei wählbaren
Namen, die dann als Selektor im weiter unten genannten switch - case Konstrukt dient und die verschiedenen
UINT Konstanten, wie WM_PAINT nacheinander enthält.
Die Funktion WndProc
In der WndProc kann ich mit DrawText oder TextOut meinen Text als Characterstrings ausgeben lassen.
Wie wir aus der einleitenden Programmierung in C wissen, sind Strings intern als Arrays aus
Einzelbuchstaben(char) realisiert, die jeweils mit der Escape - Sequenz "\0" enden. Daher steht manchen
Variablennamen auch die Zeichenfolge "sz"(string zero) voran.
Unter Anderen erhält DrawText als Argumente einige Konstanten, deren Namen mit "DT_"(Draw Text) eingeleitet
sind, die durch bitweises Oder " | " miteinander verknüpft sind.
Dadurch werden einzelne Bits gesetzt für die Art und Weise, wie der Text dargestellt wird.
Diese bitweise - Oder - Verknüpfungen finden sich auch in der WinMain Funktion als Konstanten, deren
Namen mit "CS_" eingeleitet werden(für Class Style), wie CS_HREDRAW für horizontales Neuzeichnen, oder
CS_VREDRAW für vertikales Neuzeichnen.
Das switch - case Konstrukt in der WndProc
In der Funktion WndProc befindet sich ein switch - case Konstrukt, das als Selektor eine Variable
vom Typ UINT verwendet, in die nacheinander die Konstanten geschrieben werden, die in der windows.h - Datei
definiert wurden und die für bestimmte Ereignisse stehen, wie WM_PAINT, WM_DESTROY, WM_LBUTTONDOWN,
WM_LBUTTONUP, WM_LBUTTONDBLCLK u.v.A.
"WM_" steht hier jeweils für "Windows Message".
PARAMETERÜBERGABE
Die Parameterübergabe an einzelne Funktionen ist ziemlich komplex.
So erhält die Funktion WndProc :
1) eine Variable vom Typ HWND, den Windows - Handler,
2) eine Variable vom Typ UINT, die die eigentlichen Nachrichten enthält,
3) eine Variable vom Typ WPARAM,
4) eine Variable vom Typ LPARAM.
Alle Bezeichner für diese Variablen sind frei wählbar. Ich habe sie daher im Beispielprogramm
ebenfalls durch unsinnige Wörter ersetzt.
Die Windows - Standard - Funktion DrawText bekommt :
1) eine Variable vom Typ HDC(Handle Device Context), zu vergleichen mit einer neuen Leinwand,
2) den String der ausgegeben werden soll(natürlich immer in Anführungszeichen),
3) eine -1, jede andere Zahl hat im Test das Verschwinden des Strings zur Folge gehabt(!),
4) eine Referenz auf ein Rechteck vom Typ RECT,
5) noch 3 durch bitweises Oder getrennte Windows - Konstanten mit "DT_" am Anfang für "DrawText",
die von links nach rechts die Bedeutungen
- Einfügemodus
- horizontale Ausrichtung
- vertikale Ausrichtung
haben.
Die Windows - Standard - Funktion TextOut bekommt
1) eine Variable vom Typ HDC(Handle Device Context), zu vergleichen mit einer neuen Leinwand,
2) und 3) zwei UINT Werte für x und y als Einfügemarken,
4) den String selbst und
5) die Länge des Strings, den man sofern man nicht stundenlang Buchstaben zählen möchte, tunlichst mit
der Methode strlen der Klasse string ermittelt, Bsp.:
TextOut(hdc, 25, 20, "Klaus Weyell", strlen("Klaus Weyell"));
Die Funktion TextOut kann auch in Verbindung mit der Funktion sprintf genutzt werden.
Dafür muss die C - Header - Datei stdio.h eingebunden werden. Konkret sieht das dann so aus:
f=sprintf(d_mauz, "Doppelklick(links): %d ", D_mauszaehl);
TextOut(hdc, 25, 290, d_mauz, f); /*wobei ich d_mauz und D_mauszaehl global und f in der Funktion
deklariert habe.*/
TextOut kann auch, so wie hier, sprintf direkt integrieren :
TextOut(hdc, 20, 40, text, sprintf(text, "(x, y) = %d, %d ", LOWORD(lParam), HIWORD(lParam)));
erstes Windows - Beispielprogramm
Kleines Intermezzo über Shareware
Für eine 30 Tage Version irgendeines Programms sollte man das Installationsdatum verschlüsselt in eine
mit dem Programm erstellte Datafile schreiben, z.B. Install.log o.Ä. Das Programm sollte sich schon weigern,
ohne diese beim Installieren angelegte Datei zu laufen - und das Datum danach wieder auslesen.
Freischalten: Ein bestimmtes Codewort aktiviert Funktionen, die ohne das Codewort via if - Abfrage
übersprungen werden. Schön und gut, aber ich will ja nicht, dass jeder das selbe Codewort verwendet!
Also lege ich ein langes, z.B. neunstelliges Codewort an und frage nur die Länge, sowie bestimmte Stellen
wie etwa die 2., 3. und 9. Stelle ab, während die anderen Eingaben beliebig bleiben.
Ebenso ist es möglich, sich über einen bestimmten Algorithmus gültige Codewörter, z.B. aus Namen und
Geburtsdatum(je nach Wunsch mit einem zusätzlichen vorgegebenen Schlüssel) bilden zu lassen, wobei dann
aber darauf zu achten wäre, dass es keine mögliche Kombination aus Name und GebDat gibt, die nicht akzeptiert
wird. Ich finde, man dürfte solche Namen wie Meier2 und Geburtsdaten vor 1900 ausschließen.
dynamischer Briefumschlag
Mausklickzählprogramm
Windows Programmierung mit MFC