home

teaching

klasse 00-I

klasse 00-I
3. Semester

klasse 00-I
5. semester

klasse 01-I

klasse 01-I
3. semester

 

contact

 

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;
}