Arbeiten mit Strings

  • Code
  • Erklärung
  • Ergebnis

#include <iostream>
#include <string>
using namespace std;


int main()
{
    string first = "Einfaches Beispiel ";
    string second = "von Strings.";
    string third = first + second;
    cout << third << endl;

    //Find 
    string s1 = "Das ist ein Beispiel Text.";
    string s2 = "ein";
    bool exists = s1.find(s2);
    cout << "'" << s1 << "' beinhaltet '" << s2 << "': " << exists << endl;
    //Substring
    cout << "'" << s1 << "' ab der Position 5 lautet: " << s1.substr(5) << endl;

    //Compare
    cout << "'" << s1 << "' und '" << s2 << "' ist gleich: " << s1.compare(s2) << endl;

    //Replace
    string s = "abc----hij";
    cout << "\nIndex: 0123456789" << endl;
    cout << "1)     " << s << endl;
    cout << "2)     " << s.replace(3, 4, "") << endl; //Bindestriche entfernen


    //Erase
    string withSpaces = "     Leerzeichen Enthalten   ";

    withSpaces.erase(0, withSpaces.find_first_not_of(" ")); //Leerzeichen am Anfang entfernen 
    withSpaces.erase(withSpaces.find_last_not_of(" ") + 1);  //Leerzeichen am Ende entfernen 
    cout << "Getrimmt: " << withSpaces << endl;

    // Caps Lock umdrehen
    string wrong = "uNABSICHLICHcAPSlOCKeINGESCHALTET.";
    string correct;

    for (int i = 0; i < wrong.size(); i++)
    {
        if (islower(wrong[i]))
            correct += toupper(wrong[i]);

        if (isupper(wrong[i]))
            correct += tolower(wrong[i]);
    }

    cout << "\nOriginal: " << wrong << endl;
    cout << "Korrigiert: " << correct<< endl;


    return 0;
}


Die String-Klasse ist eine der am häufigsten genutzten Klassen mit vielen Methoden und Verwendungsmöglichkeiten, weshalb wir ihr an dieser Stelle einen eigenen Bereich widmen.
Wie bereits in den Grundlagen beschrieben werden Strings wie gewöhnliche Variablen initialisiert und deklariert. Auch das Zusammenführen von mehreren Strings ist denkbar einfach. Hierzu wird einfach der + Operator verwendet um zwei String aneinander zu hängen. Wir möchten an dieser Stelle nur die wichtigsten Eigenschaften der Stringklasse erwähnen die man als angehender Entwickler kennen sollte. Für eine komplette Dokumentation verwende bitte eine vollständige Referenz

Zu den wichtigsten Methoden des Stringklasse zählen:
  • Length (liefert die Länge des Strings)
  • Find (prüft ob ein substring in einem String enthalten ist)
  • Substr (liefert einen Teilstring ab einer definierten Position)
  • Compare (prüft ob sich zwei Strings gleichen. Groß- und Kleinschreibung wird berücksichtigt)
  • Replace (ersetzt ab einer bestimmten Position, für eine bestimmte Länge Zeichen mit einem bestimmten Wert)
  • ToUpper / ToLower (Liefert den Character groß oder klein geschrieben)
  • Erase (Entfernt bestimmte Zeichen zu Beginn oder Ende des gegebenen Strings)

Klassen

  • Code
  • Erklärung
  • Ergebnis

#include <iostream>
#include <string>

using namespace std;


class Car
{
//Klassenvariablen
private:
    string brand;
    string model;
    int power;
    string color;

public:
    //Konstruktor
    Car(string s)
    {
        brand = s;
    }

    void StartEngine()
    {
        cout << "Der " << brand << " faehrt los." << endl;
    }

    string GetColor()
    {
        return color;
    }

    void SetColor(string s)
    {
        color = s;
    }
};


int main()
{
    Car myCar("Mitsubishi");

    myCar.SetColor("Schwarz");
    myCar.StartEngine();

    cout << "Die Farbe des Autos ist " << myCar.GetColor() << endl;
}



Bevor du dich mit dem folgenden Beispiel widmest, mache dich bitte mit dem Konzept von objektorientierter Programmierung vertraut. Ein klares Verständnis von Klassen ist für einen Programmierer unabdinglich. Einen passende Quelle bietet unser Artikel zu Objektorientierung.

Zu Beginn widmen wir uns der Instanziierung einer Klasse und die Verwendung ihrer Methoden.
In diesem Beispiel haben wir eine Klasse "Car" erstellt. Zu Beginn definieren wir einige Variablen die wir als private deklarieren. Den Zugriff auf diese Variablen ermöglichen wir über sogenannte getter und setter. Es ist empfohlen Klassenvariablen stets über deren Eigenschaftsmethoden erreichbar zu machen. Der Vorteil daran ist unter anderem, dass sich so leicht Prüfungen auf die Korrektheit der Werte implementieren lässt und der Zugriff klar gesteuert werden kann.

Jede Klasse besitzt von Haus aus einen Standardkonstruktor der uns unser Objekt instanziiert. Der Konstruktor besitzt keinen typischen Rückgabewert und muss immer den Klassennamen als Bezeichnung haben. In diesem Besipiel haben wir den Standardkonstruktor mit einem String-Parameter erweitert um zu gewährleisten, dass jedes Auto-Objekt zumindest einen Markennamen beinhaltet. Der Standardkonstruktor wird somit ersetzt und existiert nicht mehr.

Solltest du weiterhin einen Parameterlosen Konstruktor benötigen, muss dieser als public Car() {} implementiert werden. Zuletzt besitzt diese Klasse noch eine einfache Methode StartEngine() die eine Ausgabe in der Console veranlasst.

In unserer Main-Methode können wir nun ein neues Autoobjekt erschaffen. Wir nennen es "myCar" und übergeben dem Konstruktor „Mitsubishi“ als Markennamen. Mit einem einfachen Punk können wir nun auf die Eigenschaften und Methoden unseres Objektes zugreifen.

Vererbung

  • Code
  • Erklärung
  • Ergebnis

#include <iostream>
#include <string>

using namespace std;


class Kfz
{
//Klassenvariablen
protected:
    string brand;
    string model;
    int power;
    string color;

public:
    virtual void StartEngine() = 0;

    string GetColor()
    {
        return color;
    }

    void SetColor(string s)
    {
        color = s;
    }
};

class Pkw : public Kfz
{
private:
    int numDoors;

public:
    int GetNumDoors()
    {
        return numDoors;
    }
    void SetNumDoors(int value)
    {
        numDoors = value;
    }

    Pkw(string s)
    {
        brand = s;
    }

    void StartEngine()
    {
        cout << "Der PKW " << brand << " startet." << endl;
    }

};

class Lkw : public Kfz
{
private:
    int maxWeight;

public:
    int GetMaxWeight()
    {
        return maxWeight;
    }
    void SetMaxWeight(int value)
    {
        maxWeight = value;
    }

    Lkw(string s)
    {
        brand = s;
    }

    void StartEngine()
    {
        cout << "Der LKW " << brand << " startet." << endl;
    }

};



int main()
{
    Pkw myPKW("Mitsubishi");
    Lkw myLKW("MAN");


    myPKW.SetColor("Schwarz");
    myPKW.StartEngine();

    myLKW.SetMaxWeight(50);
    myLKW.StartEngine();

    cout << "Die Farbe des PKW ist " << myPKW.GetColor() << endl;
    cout << "Das maximale Gewicht des LKW ist " << myLKW.GetMaxWeight() << " Tonnen" << endl;

    return 0;
}


Bevor du dich mit dem folgenden Beispiel widmest, mache dich bitte mit dem Konzept von objektorientierter Programmierung vertraut. Ein klares Verständnis von Klassen ist für einen Programmierer unabdinglich. Einen passende Quelle bietet unser Artikel zu Objektorientierung.

Für dieses Beispiel verwenden wir die Klasse von vorhin benennen sie aber in Kfz um. Wir erweitern die Methode StartEngine() um das Schlüsselwort virtual und einem = 0; um sie in den erbenden Klassen zu überschreiben und so eine Typeneigene Implementierung zu haben. Somit wurde auch sicher gestellt, dass alle von Kfz erbenden Klassen diese Methode implementieren. Gleichzeitig haben wir auch die Instanziierung eines undefinierten Kfz Objektes verhindert da es sich hierbei um einer pure virtuelle Methode handelt. Sobald eine Klasse mindestens eine solch virtuelle Methode enthält wird sie abstrakte Klasse genannt und ist nicht mehr instanziierbar. Wie du siehst enthält jede erbende Klasse die Eigenschaften ihrer Basisklasse. Somit können wir die Farbe eines PKW aufrufen obwohl die PKW-Klasse selbst keine Implementierung beinhaltet. Somit lassen sich leicht gemeinsame Eigenschaften vom LKW und PKW in einer gemeinsamen Basisklasse implementieren.

Polymorphie

  • Code
  • Erklärung
  • Ergebnis

#include <iostream>
#include <string>

using namespace std;


class Kfz
{
//Klassenvariablen
protected:
    string brand;
    string model;
    int power;
    string color;

public:
    virtual void StartEngine() { }; 

    string GetColor()
    {
        return color;
    }

    void SetColor(string s)
    {
        color = s;
    }
};

class Pkw : public Kfz
{
private:
    int numDoors;

public:
    int GetNumDoors()
    {
        return numDoors;
    }
    void SetNumDoors(int value)
    {
        numDoors = value;
    }

    Pkw(string s)
    {
        brand = s;
    }

    void StartEngine()
    {
        cout << "Der PKW " << brand << " startet." << endl;
    }

};

class Lkw : public Kfz
{
private:
    int maxWeight;

public:
    int GetMaxWeight()
    {
        return maxWeight;
    }
    void SetMaxWeight(int value)
    {
        maxWeight = value;
    }

    Lkw(string s)
    {
        brand = s;
    }

    void StartEngine()
    {
        cout << "Der LKW " << brand << " startet." << endl;
    }

};

class Motorrad : public Kfz
{
private:
    int maxSpeed;

public:
    int GetMaxWeight()
    {
        return maxSpeed;
    }
    void SetMaxWeight(int value)
    {
        maxSpeed = value;
    }

    Motorrad(string s)
    {
        brand = s;
    }

    void StartEngine()
    {
        cout << "Das Motorrad " << brand << " startet." << endl;
    }

};



int main()
{
    Kfz* arr[3];

    arr[0] = new Pkw("Mitsubishi");
    arr[1] = new Lkw("MAN");
    arr[2] = new Motorrad("Yamaha");

    for (Kfz* tmp : arr)	
    {
        tmp->StartEngine();
    }

    return 0;
}


Bevor du dich mit dem folgenden Beispiel widmest, mache dich bitte mit dem Konzept von objektorientierter Programmierung vertraut. Ein klares Verständnis von Klassen ist für einen Programmierer unabdinglich. Einen passende Quelle bietet unser Artikel zu Objektorientierung.

Die Polymorphie ermöglicht uns, dass Objekte gemeinsamer Basisklasse, je nach ihrer eigenen Implementierung, unterschiedlich auf den gleichen Befehl reagieren. Somit können wir, wie dem vorherigen Beispiel entsprechend, unterschiedliche Kfz-Objekte erstellen und gemeinsam ansprechen ohne dabei die konkrete Implementierung der einzelnen Klassen aufrufen zu müssen.

Wir verwenden wieder den Code des vorherigen Beispiels und erweitern ihn wie folgt:
Zuerst erstellen wir eine Motorradklasse die ebenfalls von Kfz erbt und eine eigene StarteMotor() Implementierung hat.
Als nächstes befüllen wir ein kurzes Array mit Kfz-Objekten und lassen per foreach-Schleife die StarteMotor() Methode aufrufen. Beachte, dass wir hier mit Pointern arbeiten müssen, da die Kfz-Klasse nach wie vor abstrakt ist und keine Instanziierung des Basisklasse erlaubt.
Wie du siehst wird die korrekte Implementierung der jeweiligen Klasse aufgerufen.

Interfaces

  • Code
  • Erklärung
  • Ergebnis

class IMoveable
{
public:
    virtual void Move() = 0;
    virtual void Stop() = 0;
    virtual void Explode() = 0;
};

class Kfz : IMoveable 
{
private:
    int wheels;

public:
    void Move()
    {
        cout << "Kfz bewegt sich" << endl;
    }

    void Stop()
    {
        cout << "Kfz stoppt" << endl;
    }

    void Explode()
    {
        cout << "Kfz explodiert" << endl;
    }
};

Im Gegensatz zu C# oder Java bietet C++ keine Interfaces die ein eigenes Schlüsselwort besitzen. Vielmehr ist in C++ ein Interface eine Klasse die nur aus rein virtuellen Methoden besteht.
Wie oben bereits erwähnt, wenn eine Klasse zumindest eine reine virtuelle Methode enthält wird sich selber nicht mehr instanziierbar.
Interfaces enthalten im Gegensatz zu abstrakten Klassen überhaupt keine implementierten Methoden sondern existieren nur als Funktionssignaturen die von erbenden Klassen implementiert werden MÜSSEN. Somit wird sichergestellt, dass alle Klassen des gleichen Basistyp oder zumindest des gleichen Interfaces, die gleichen Methoden zur Verfügung haben.

Üblicher Weise beginnen Interfaces immer mit einem großen i.
Als Beispiel erstellen wir ein einfaches Interface IMoveable das sich wunderbar für unsere Klassen oben eignen würde.

Streams

  • Code
  • Erklärung
  • Ergebnis

#include <fstream>
#include <string>
#include <iostream>
#include <vector>
using namespace std;

int main()
{
	//Erstellung einer Liste mit Strings
	vector<string> list = { "Das", "ist", "ein", "Beispiel", "mit Text" };

	//Pfad wo unser .txt-File erstellt und ausgelesen werden soll
	string path = "F:\\Temp\\Testfile.txt";

	ofstream out(path);

	//Durchlauf jedes Eintrags der Liste und Schreiben in das File
        for (string s : list)
        {
            out << s << endl;
        }
        out.close();

	//Einlesen des Files
	ifstream in(path);
	string currentLine;

	while (getline(in, currentLine))
	{
            cout << currentLine << endl;
	}
	in.close();

	//Kopieren des Textfiles als Backup
	string copyPath = path + "_BKP";
	ifstream  from(path, ios::binary);
	ofstream  to(copyPath, ios::binary);
	to << from.rdbuf();

	from.close();
	to.close();

	return 0;
}

Um in C++ Dateien ein- und auslesen zu können, bedient man sich sogenannter Streams. Der passende Input- und Outputstream findet sich in dem Header fstream. In diesem Beispiel wollen wir uns den Datentypen ifstream (InputFileStream) und ofstream (OutputFileStream) widmen um Texte in eine Datei zu schreiben und wieder auszulesen.
Anschließend werden wir ein einfaches Backup davon erstellen.

Zuerst erstellen wir uns eine einfache Liste mit Strings die wir uns später in ein Textfile ablegen lassen wollen. Bei der Pfadangabe müssen wir beachten den Backslash entsprechend zu escapen, dass der Pfad erreichbar ist und wir passende Berechtigungen haben (zB. auf Netzwerkpfade).
Die Erstellung eines neuen StreamWriter-Objektes ist denkbar einfach. Alles was wir dafür brauchen ist unser Dateipfad.
In der Nachfolgenden foreach-Schleife gehen wir nun jeden String der Liste durch und schreiben diesen per out << in unser Textfile. Um den Text Zeilenweise auszugeben, fügen wir ein endl; hinten an.
Am Ende des Vorgangs bitte nicht vergessen per close() den ofstream wieder zu schließen um beanspruchte Ressourcen freizugeben.
Der ifstream funktioniert sehr ähnlich. Bei der Initialisierung benötigen wir ebenfalls nur den Dateipfad. Um während des Auslesens nicht über den eigentlichen Textumfang hinaus zu laufen, prüfen wir in der while-Schleife ob noch weitere Zeilen im Textfile enthalten sind. Wenn ja, dann geben wir diesen per cout << Zeilenweise in der Konsole aus.
Auch hier schließen wir den Stream per close().

Abschließend erstellen wir eine Kopie der eben erstellten Datei. Auch hier können wir uns der if-/ofstreams bedienen. Übergeben wir einen Quell- und Zielpfad können wir per to << from.rdbuf(); eine entsprechende Kopie erstellt.
Auch hier werden wieder beide Streams geschlossen.

Operatoren I

  • Code
  • Erklärung
  • Ergebnis

//this -Operator
class Mitarbeiter 
{ 
    private: 
    string vorname; 
    string nachname; 

    public: 
    //Konstruktor 
    Mitarbeiter(string vorname, string nachname) 
    { 
        // Name des Objektes Mitarbeiter wird mit den übergebenen Parametern befüllt 
        this->vorname = vorname; 
        this->nachname = nachname; 
        } 
}; 

// ?: -Operator (verkuerztes if)
//Bedingung ? Erste_Aweisung(true) : Zweite_Anweisung(false); 
int a = 5; 
int b = 7; 
 
int bigger = (a > b) ? a : b; // b wird zugewiesen 
 
 
// % -Operator (Modulo) 
// Rest einer Division
int a = 5; 
a = a % 3; //(oder auch a %= 3) 
//a=2 

Der .this -Operator
Der .this Operator verweist auf die Verwendung der Variable (oder Methode) des aktuell instanzierten Objektes. Besonders hilfreich bei der Verwendung von Klassenmethoden die Variablen mit dem gleichen Namen wie Klassenvariablen enthalten.

?: -Operator
Der ?: -Operator bietet eine verkürzte Schreibweise der klassischen if-Bedingung in der Form (Bedingung) ? (Anweisung wenn True) : (Anweisung wenn false);

% -Operator (Modulo)
Das Modulo ist der Rest einer Division (5 / 3 = 1 -> 2 Rest). Einfaches Anwednungsbeispiel: prüfen ob eine Zahl gerade oder ungerade ist.