|
Tipps und Fragen |
Inhaltsverzeichnis |
|
|
Menu |
Bei Konsolenprojekten braucht man häufig eine Möglichkeit
ein Menu darzustellen, bei dem der Benutzer mit einer Zahl eine Funktion
auswählen kann. Bei der Musterlösung für die Übung
vom Abend 17 im 1. Semester
habe ich eine Klasse Menu geschrieben, die einen
Weg zeigt, wie man so etwas sauber und objektorientiert lösen
kann. |
|
Die Klasse Menu sieht so aus: |
|
#ifndef MENU_H
#define MENU_H
#include <string>
using std::string;
const long MaxMenu = 50;
class Menu
{
public:
Menu();
~Menu();
bool addMenuEntry(const string& menuEntry);
void show();
int getUserInput();
private:
string m_menuEntries[MaxMenu];
int m_entryCount;
};
#endif
|
|
Man erzeugt ein Objekt der Klasse Menu und ruft dann "addMenuEntry"
auf, um die verschiedenen Optionen einzufüllen. Danach kann man
das Menu jederzeit mit "show" darstellen und mit "getUserInput"
ist es möglich auf eine Benutzereingabe zu warten. |
|
Schau Dir das Beispiel mit der CDListe an, da siehst du wie die
Klasse verwendet wird. Die Klasse kann noch stark verbessert werden
indem man mit der Console Klasse noch versucht die Ausgabe schöner
zu gestalten. |
|
Hier das Beispiel mit der CD-Liste und der Menu Klasse zum Download
(.zip) |
|
Zahlen in Strings umwandeln |
Manchmal hat man gewisse Daten als Zahlen und möchte diese
aber gerne als Strings darstellen. Mit der STL verwenden wir das ständig
wenn wir schreiben: |
|
#include <iostream>
int main()
{
int test = 1234;
std::cout << test << std::endl;
return 0;
}
|
|
Die Daten, die auf dem Bildschirm erscheinen sind nicht direkt die
Daten, wie sie im int-Datentyp gespeichert werden, sondern der int
wird zuerst in eine leserliche Form, also in einen String umgewandelt. |
|
Diese Umwandlung in eines String beherrschen alle stream Klassen
der STL, wovon ja das cout und das cin Objekt ableiten. Wir brauchen
eigentlich nur einen stream, der den String anstatt auf der Konsole
auszugeben, einfach im Speicher aufbewahrt und wieder zur Verfügung
stellt. |
|
Eine solche Klasse ist die std::stringstream Klasse: |
|
#include <iostream>
#include <sstream> // für die Klasse stringstream
int main()
{
int test = 1234;
std::stringstream stream;
// int in den stream
// hineinschreiben
stream << test;
std::string testString;
// Inhalt des streams
// als string bereitstellen
stream >> testString;
return 0;
}
|
|
Strings in Zahlen umwandeln |
Der umgekehrte Weg, bei dem man Strings in Zahlen umwandeln will,
funktioniert analog zum oberen Beispiel. |
|
#include <iostream> #include <sstream> // für die Klasse stringstream
int main()
{
std::string testString("1234");
std::stringstream stream;
// string in den stream
// hineinschreiben
stream << testString;
int test = -1;
// Inhalt des streams
// als int bereitstellen
stream >> test;
return 0;
}
|
|
Ist die Umwandlung nicht möglich, bleibt die Variable test
unverändert. |
|
Bitkombinationen |
Frage
Ich habe folgendes Problem:
Ich habe eine Kombination mit 2 Bytes = 16 bit z.b. FF FF.
Mein wunsch ist alle mögliche Kombinationen auf die Konsole
zu bringen, wie mache ich das?
|
|
Antwort
Hier ein Beispiel mit 16 Bit also 2 Bytes, von dem was du brauchst: |
|
Dezimalzahl HEX BINÄR
0 00 00 0000000000000000
1 00 01 0000000000000001
2 00 02 0000000000000010
3 00 03 0000000000000011
...
15 00 FF 0000000000001111
...
566 02 36 0000001000110110
...
65535 FF FF 1111111111111111
|
|
Das heisst du musst eigentlich nur von 0 bis 2^(Anzahl Bits)-1 durchzählen,
und du erwischt alle möglichen Kombinationen! Im Code wird es
sogar sehr einfach!
Der unsigend short Datentyp hat genau 16 Bit und würde sich dafür
eignen. Brauchst du hingegen 32 Bit also 4 Byte nehmen wir den unsigned
long.
Falls du auch eine binäre Darstellunbg brauchst, braucht es etwas
mehr. Du findest alles im Beispiel: |
|
#include <iostream> // für cout #include <iomanip> // für setbase, setw #include <limits> // für numeric_limits #include <bitset> // für die Klasse bitset #include <string> // für die Klasse string using namespace std; int main() { // mit setw wird die Breite eines // Feldes gesetzt cout << setw(11) << "Dezimal"; cout << setw(15) << "Hexadezimal"; cout << setw(20) << "Binaer"; cout << endl; // numer_limits<NumerischerDatentyp> ist eine // Klasse, die in der Datei limits definiert // ist und Grenzen zum Datentyp enthält for(unsigned short i = 0;
i <= numeric_limits<unsigned short>::max();
++i) { // mit setbase wird die Basis // für die Zahlenausgabe gesetzt cout << setw(11); cout << setbase(10); cout << i; cout << setw(15); cout << setbase(16); cout << i; // für die Binäre Ausgabe brauchen // wir die Klasse std::bitset<AnzahlBits> std::bitset<16> bits(i); cout << setw(20); cout << setbase(2); std::cout << bits.to_string(); cout << endl; } return 0; }
|
|
|
Eine HtmlDocument
Klasse |
Um eine schön formatierte Ausgabe von Daten zu erhalten,
verwendet man im Normalfall ein Programm wie eine Tabellenkalkulation
oder ein Textverarbeitungsprogramm. Um aber aus einer Konsolenapplikation
eine vernünftige Darstellung zu erhalten, bietet sich die Möglichkeit
eine HTML Datei zu schreiben, die nachher in einem beliebigen Internet
Browser angesehen werden kann.
Die Klasse, die ihr herunterladen könnt dient als Ausgangspunkt
um objektorientiert an ein HTML Dokument zu gelangen.
Die Version, die zur Verfügung steht, bietet unter Windows
die Möglichkeit die Datei direkt im Browser anzusehen oder
zu drucken.
Die erste Version verwendet Templates zum Erzeugen von Tags. Diese
Version kompiliert nur mit dem Visual C++ .Net. Dateien herunterladen
(.zip)
Hier die zweite Version. Diese funktioniert nun
auch mit dem alten Visual Studio (Version 6). (.zip)
Für die Version für das Visual Studio 6 gibt es nun eine
neue Version, mit der man auch Attribute setzen kann (nützlich
bei Tabellen). Sieh Dir das Beispiel an. Möglicherweise ist
die neue Version nicht ganz kompatibel zur alten, darum bleibt diese
weiterhin verfügbar. (.zip) |
|
|
Zwei Werte als Rückgabewert |
Frage
Wie kann ich mehr als einen Wert zurückgeben? geht das?
Bsp.
int TestRueckgabe()
{
return ParameterA && ParameterB; // geht nicht
}
--> Wie kann ich dann die Beiden Parameter anwählen?
( erstes mit int Test = TestRueckgabe() --> aber wie den zweiten??)
|
|
Antwort
Wenn eine Funktion oder eine Methode mehr als ein Ergebnis als
Rückgabewert hat, dann kannst du diese Ergebnisse in der Parameterliste
als "Out"-Argumente in der Parameterliste haben. Du musst
diese dann einfach als Referenzen oder Zeiger übergeben, wie
zum Beispiel in der Swap-Funktion (siehe
den Stoff dazu). |
|
void TestRueckgabe(int& a, int& b)
{
a = ParameterA;
b = ParameterB;
}
|
|
Aufruf:
main()
{
int a = 0;
int b = 0;
TestRueckgabe(a, b);
return 0;
}
|
|
Aber:
Vielleicht gehören diese Daten einfach zusammen! Das heisst
du willst eigentlich nicht zwei einzelne Elemente auf einmal erhalten,
sondern, die beiden ergeben zusammen eine Abstraktion von irgend
etwas.
Das würde bedeuten, dass man diese beiden Dinge zusammen in
eine Klasse oder mindestens eine Struktur packen müsste! |
|
struct Koordinaten
{
int X;
int Y;
};
Koordinaten TestRueckgabe()
{
Koordinaten ergebnis;
ergebnis = ParameterA;
ergebnis = ParameterB;
return ergebnis;
}
|
|
|
Icon für eine Konsolenapplikation |
Es ist möglich einer eigenen Konsolenapplikation ein eigenes
Icon zu geben. Im Normalfall haben unsere Applikationen, also die
.exe-Dateinen kein spezielles Icon, wenn man sie im Explorer betrachtet.
Icons, die vom Windows-Betriebssystem verwendet werden, um im Explorer
ein Bild anzuzeigen befinden sich in den so genannten Ressourcen
eines Programms. Eine Konsolenapplikation besitzt zunächst
keine Ressourcen, wir können diese aber nachträglich hinzufügen.
Hier also eine kleine Anleitung, wie man im Visual Studio 6 ein
Icon zu einem Kosnolenprojekt hinzufügt (sollte im Visual Studio
.Net ähnlich funkt:
Öffne das Projekt, für welches du ein Icon erstellen
willst mit dem Visual Studio. Wähle im Menu "Einfügen"
den Punkt "Ressource...". |
|
|
|
Nun hast du die Möglichkeit mit dem einigen Hilfsmitteln
ein kleines Bild zu malen (32x32 Pixel).
Wenn du in diesem Ressourcen-Editor Ctrl+S drückst um Deine
Arbeit zu speichern, bietet der "Speichern als..." Dialog
die Möglichkeit eine Datei mit der Endung .rc zu speichern.
Speichere diese Datei im Projektverzeichnis.
Die Datei ist noch nicht im Projekt, kann aber wie .cpp oder .h
Dateien in das Projekt eingefügt werden. (Zum Beispiel über
rechte Maustaste in der Dateiansicht oder über das Menu "Projekt",
"Zum Projekt hinzufügen".
Danach hat Dein Projekt in der Ansicht zusätzlich zur Klassenansicht
und zur Dateiansicht eine Ressourcen-Ansicht. Darin findest du ein
Verzsichnis "Icon", in dem sich Dein Icon mit einer ID
"IDI_ICON1".
Wenn du das Projekt nun neu kompilierst erhält es Ressourcen
und Windows XP verwendet diese Icon Ressource um im Explorer ein
Bild darzustellen.
Das Visual Studio legt übrigens für diese Ressource eine
.ico Datei an. Diese Datei kannst du auch natürlich mit anderen
Programmen bearbeiten. Du musst aber danach das Programm neu kompilieren.
Es ist auch möglich zusätzlich für ein anderes "Device"
ein kleineres Icon zu erstellen (16x16 Pixel). Wenn du das Bild
im Visual Studio bearbeitest ist oben am Editor-Fenster eine Combobox
in der du das zu bearbeitende Icon auswählen kannst. Rechts
daneben ist ein Knopf, mit dem eine neue Grösse erstellt werden
kann. |
|
|
|
Das Betriebssystem hat so die Wahl, um das jeweils geeignete
Icon auszuwählen.
Beachte auch, dass bei der Auswahl der Werkzeuge auch eine Farbauswahl
möglich ist. Als Farbe wird auch die Bildschimhintergrundfarbe
angeboten (sieht aus wie ein kleiner Monitor). Diese Farbe wird
dann Transparent, also nicht dargestellt. |
|
|
Sicheres Einlesen einer Zahl von der Konsole |
Das Einlesen einer Zahl von der Konsole scheint ein leichtes Unterfangen.
Im Normalfall scheint die folgende Lösung gut genug: |
|
#include <string> // die string Klasse #include <iostream> // das cin und das cout Objekt
// wir definieren eine ungueltigeZahl
const long ungueltigeZahl = -1;
int main()
{
long benutzerZahl = ungueltigeZahl;
while(benutzerZahl == ungueltigeZahl)
{
std::cout << "Bitte gueltige Zahl eingeben : ";
std::cin >> benutzerZahl;
}
std::cout << "die gueltige Zahl ist : ";
std::cout << bentzerZahl;
return 0;
}
|
|
So weit so gut. Lass das Programm laufen und gib etwas anderes
ein als eine Zahl. Unser Programm spielt nun verrückt und gibt
auf immer und ewig "Bitte gueltige Zahl eingeben : " aus.
Wir haben kaum eine Möglichkeit einzugreifen und stoppen das
Programm entnervt.
Das Problem ist, dass das Extrahieren der Zahl in der Zeile mit
dem cin misslingt. Dabei bleibt aber das Eingegebene "Return"
in cin Stream drin. Immer und immer wieder wird danach versucht
eine Zahl zu extrahieren, was weiterhin misslingt, das ungültige
Zeichen bleibt aber im stream drin.
In einigen Fällen, vielleicht sogar in den meisten Fällen
könnte folgendes Vorgehen einen Erfolg bringen: |
|
#include <string> // die string Klasse #include <sstream> // die stringstream Klasse #include <iostream> // das cin und das cout Objekt
// wir definieren eine ungueltigeZahl
const long ungueltigeZahl = -1;
int main()
{
long benutzerZahl = ungueltigeZahl;
while(benutzerZahl == ungueltigeZahl)
{
std::cout << "Bitte gueltige Zahl eingeben : ";
// string zum Aufnehmen der // Benutzereingabe std::string eingabe; // Auf jeden Fall ganze Zeile aus // dem stream lesen std::getline(std::cin, eingabe); // Wir erzeugen nun // einen stringstream aus // der Eingabe std::stringstream stream(eingabe);
stream >> benutzerZahl;
}
std::cout << "die gueltige Zahl ist : ";
std::cout << bentzerZahl;
return 0;
}
|
|
Wir lesen jetzt also die Benutzereingaben immer in einen string
ein, was fast immer gelingen sollte, der cin-Stream bleibt danach
leer. Erst in einem zweiten Schritt wandeln wir die Eingaben in
eine Zahl um.
Es ist auf diese Weise auch möglich die Benutzereingabe weiter
auszuwerten. Man kann den eingegebene string mit zum Beispiel "x"
vergleichen, so dass der Benutzer die Eingabe abbrechen kann. |
|
#include <string>
#include <sstream>
#include <iostream>
// wir definieren eine ungueltigeZahl
const long ungueltigeZahl = -1;
int main()
{
long benutzerZahl = ungueltigeZahl;
while(benutzerZahl == ungueltigeZahl)
{
std::cout << "Bitte gueltige Zahl eingeben : ";
// string zum Aufnehmen der
// Benutzereingabe
std::string eingabe;
// Auf jeden Fall ganze Zeile aus
// dem stream lesen
std::getline(std::cin, eingabe);
if(eingabe == "x")
{
// Abbruch erwünscht
break;
}
// Wir erzeugen nun
// einen stringstream aus
// der Eingabe
std::stringstream stream(eingabe);
stream >> benutzerZahl;
}
if(ungueltigeZahl != benutzerZahl)
{
std::cout << "die gueltige Zahl ist : ";
std::cout << benutzerZahl;
}
else
{
std::cout << "Eingabe abgebrochen";
std::cout << std::endl;
}
return 0;
}
|