Übungsaufgaben zu Smart Pointer

Sie haben nun gelernt, was Smart Pointer sind und wie sie angewendet werden können. Im Folgenden können Sie das Gelernte mit einigen praktischen Aufgaben und Verständnisfragen überprüfen.

Praktische Aufgaben

In diesem Aufgabenteil werden Sie das bereits bekannte Train-Beispiel um die Klasse Station erweitern. Dem Namen entsprechend soll die neue Klasse die Bahnhöfe simulieren, an denen unsere Züge halten werden.

Teil 1

Legen Sie die Klasse Station an. Sie soll den Namen des Bahnhofs in der Variable p_sName speichern und einen Pointer p_pDestination besitzen, in dem der Zielbahnhof gespeichert werden soll. Überlegen Sie, welcher der Ihnen nun bekannten Smart Pointer-Arten am besten dafür geeignet ist.

zum Lösungsvorschlag

Teil 2

Im nächsten Schritt sollen einige nützliche Funktionen angelegt werden. Zunächst müssen Sie einen Konstruktor und eine getName()-Funktion erstellen.

zum Lösungsvorschlag

Teil 3

Jetzt soll der Zielbahnhof p_pDestination in der Funktion vSetDestination() gesetzt werden. Dazu übergeben Sie einen Pointer, der diesen beinhaltet. Bedenken Sie weiterhin die Wahl des Smart Pointers! Zusätzlich muss man auch den Inhalt von p_pDestination auch wieder auslesen können, deswegen müssen Sie auch eine Funktion getDestination() implementieren.

zum Lösungsvorschlag

Teil 4

Zum Schluss fügen Sie Ihre in vorherigen Aufgabenteilen geschriebenen Klassen Train, sowie deren Unterklassen PassengerTrainund FreightTrain an passender Stelle ein und führen alle Zellen (auch die Test-Zelle!) nacheinander aus, um die Richtigkeit Ihrer Lösungen zu überprüfen.

#include <iostream>
#include <string>
#include <memory>
//hier: Station.h implementieren
//Station.cpp
//hier: Konstruktor für Station
//hier: Funktion Station::getName()
//hier: Funktion Station::getDestination()
//hier: Funktion Station::vSetDestination()
//hier: Train.h einfügen
//Train.cpp
//hier: Destruktor einfügen
//hier: Train::vGoTo() einfügen
//hier: Train::vPrintProperties() einfügen
//hier: PassengerTrain.h einfügen
//PassengerTrain.cpp
//hier: Konstruktor einfügen
//hier: Funktion PassengerTrain::vPrintProperties() einfügen
//hier: Funktion PassengerTrain::fStation() einfügen
//hier: FreightTrain.h einfügen
//FreightTrain.cpp
//hier: Konstruktor einfügen
//hier: Funktion FreightTrain::vPrintProperties() einfügen
//hier: Funktion FreightTrain::vLoadCharge() einfügen
#define OPERATOR operator<<  //Diese Zeile kann in jeder IDE weggelassen werden, sie ist nur für Jupyter notwendig

std::ostream & OPERATOR(std::ostream& out, const Train& train)
{
    train.vPrintProperties(out);
    return out;
}

#undef OPERATOR   //Diese Zeile kann in jeder IDE weggelassen werden, sie ist nur für Jupyter notwendig
// Test

std::unique_ptr<PassengerTrain> aTrain;
aTrain =  std::make_unique<PassengerTrain>();

auto bTrain = std::make_unique<FreightTrain>(2, "tons of wood");

std::cout << std::endl << "Eigenschaften 'aTrain':" << std::endl;
std::cout << *aTrain;

std::cout << std::endl << "Eigenschaften 'bTrain':" << std::endl;
std::cout << *bTrain << std::endl;


    auto berlin = std::make_shared<Station>("Berlin");
    auto hamburg = std::make_shared<Station>("Hamburg");
    auto frankfurt = std::make_shared<Station>("Frankfurt");
    auto koeln = std::make_shared<Station>("Köln");
    auto muenchen = std::make_shared<Station>("München");

//rudimentäre Linie aufbauen (die bekannte Ringbahn Hamburg-Hamburg)
hamburg->vSetDestination(koeln);
koeln->vSetDestination(frankfurt);
frankfurt->vSetDestination(berlin);
berlin->vSetDestination(hamburg);

std::cout << "Guten Tag meine Damen und Herren, herzlich willkommen im Zug der Deutschen Bahn von Hamburg nach Hamburg über Köln Hbf, Frankfurt Hbf und Berlin Gesundbrunnen!" << std::endl;
std::cout << std::endl;
std::cout << "Sehr geehrte Fahrgäste, in wenigen Minuten erreichen wir Köln Hauptbahnhof. Wir verabschieden uns von allen Fahrgästen, die dort aus- und umsteigen und wünschen eine angenehme Weiterreise. Bitte denken Sie beim Aussteigen daran, Ihre persönlichen Wertgegenstände mitzunehmen. Vielen Dank!" << std::endl;
aTrain->vGoTo(koeln);
std::cout << std::endl;
aTrain->vGoTo(muenchen); //Sollte nicht funktionieren, nicht Teil der Ringbahn
std::cout << std::endl;
std::cout << "Sehr geehrte Fahrgäste, in wenigen Minuten erreichen wir Frankfurt Hauptbahnhof. Wir verabschieden uns von allen Fahrgästen, die dort aus- und umsteigen und wünschen eine angenehme Weiterreise. Bitte denken Sie beim Aussteigen daran, Ihre persönlichen Wertgegenstände mitzunehmen. Vielen Dank!" << std::endl;
aTrain->vGoTo(frankfurt);
std::cout << std::endl;
std::cout << "Sehr geehrte Fahrgäste, in wenigen Minuten erreichen wir Berlin Gesundbrunnen. Wir verabschieden uns von allen Fahrgästen, die dort aus- und umsteigen und wünschen eine angenehme Weiterreise. Bitte denken Sie beim Aussteigen daran, Ihre persönlichen Wertgegenstände mitzunehmen. Vielen Dank!" << std::endl;
aTrain->vGoTo(berlin);
std::cout << std::endl;
std::cout << "Sehr geehrte Fahrgäste, in wenigen Minuten erreichen wir Hamburg Hauptbahnhof. Unsere Fahrt endet dort, wir bitten alle Fahrgäste, auszusteigen." << std::endl;
aTrain->vGoTo(hamburg);
std::cout << "Wir bedanken uns für Ihre Reise mit der Deutschen Bahn." << std::endl;

Eigenschaften 'aTrain':
ID: 6
momentane Verspaetung: 0 Minuten
Anzahl Passagiere: 0

Eigenschaften 'bTrain':
ID: 7
momentane Verspaetung: 0 Minuten
Ladung: 2 tons of wood

Guten Tag meine Damen und Herren, herzlich willkommen im Zug der Deutschen Bahn von Hamburg nach Hamburg über Köln Hbf, Frankfurt Hbf und Berlin Gesundbrunnen!

Sehr geehrte Fahrgäste, in wenigen Minuten erreichen wir Köln Hauptbahnhof. Wir verabschieden uns von allen Fahrgästen, die dort aus- und umsteigen und wünschen eine angenehme Weiterreise. Bitte denken Sie beim Aussteigen daran, Ihre persönlichen Wertgegenstände mitzunehmen. Vielen Dank!
Zug ist nach Köln gefahren.

Der Zug kann nicht nach München fahren: Es gibt keine Verbindung.

Sehr geehrte Fahrgäste, in wenigen Minuten erreichen wir Frankfurt Hauptbahnhof. Wir verabschieden uns von allen Fahrgästen, die dort aus- und umsteigen und wünschen eine angenehme Weiterreise. Bitte denken Sie beim Aussteigen daran, Ihre persönlichen Wertgegenstände mitzunehmen. Vielen Dank!
Zug ist nach Frankfurt gefahren.

Sehr geehrte Fahrgäste, in wenigen Minuten erreichen wir Berlin Gesundbrunnen. Wir verabschieden uns von allen Fahrgästen, die dort aus- und umsteigen und wünschen eine angenehme Weiterreise. Bitte denken Sie beim Aussteigen daran, Ihre persönlichen Wertgegenstände mitzunehmen. Vielen Dank!
Zug ist nach Berlin gefahren.

Sehr geehrte Fahrgäste, in wenigen Minuten erreichen wir Hamburg Hauptbahnhof. Unsere Fahrt endet dort, wir bitten alle Fahrgäste, auszusteigen.
Zug ist nach Hamburg gefahren.
Wir bedanken uns für Ihre Reise mit der Deutschen Bahn.

Lösungen

Hier finden Sie eine mögliche Lösung zu den Aufgaben. Bitte beachten Sie, dass es mehrere Möglichkeiten gibt, die Aufgaben zu lösen, hier wird lediglich eine Variante aufgezeigt. Solange Ihre Implementierung funktioniert wie gefordert, ist sie wahrscheinlich richtig!

class Station
{
    private:
        std::string p_sName;
        std::shared_ptr<Station> p_pDestination;
    
    public:
        Station(std::string station);
    
        void vSetDestination(std::shared_ptr<Station> dest);
        std::string getName() const;
        std::shared_ptr<Station> getDestination() const;
};
Station::Station(std::string station) : p_sName(station)
{
    
}

std::string Station::getName() const
{
    return p_sName;
}
std::shared_ptr<Station> Station::getDestination() const
{
    return p_pDestination;
}

void Station::vSetDestination(std::shared_ptr<Station> dest)
{
    p_pDestination = dest;
}

Verständnisfragen

In diesem Teil wird es einige Aufgaben geben, die eher theoretischer Natur sind. Sie werden Codesnippets sehen, bei denen sich Fehler eingeschlichen haben, die Sie korrigieren sollen. Bitte führen Sie zuerst die include-Befehle aus, damit keine Zusatzfehler auftreten ;-).

#include <iostream>
#include <memory>

Aufgabe 1

Im folgenden Abschnitt soll der Inhalt eines weak_ptr ausgegeben werden. Allerdings funktioniert die Ausgabefunktion nicht. Finden Sie die Fehler und testen Sie Ihre Lösung mit der unten angegebenen Test-Funktion.

void printContent(std::weak_ptr<std::string> weak){
    std::cout << "Der Inhalt des weak-Pointers ist: " << weak << std::endl;
}
{
    auto test_shared = std::make_shared<std::string>("Test");
    auto test_weak = test_shared;
    printContent(test_weak);
}

Aufgabe 2

Es soll der Inhalt eines unique_ptr-Arrays umgekehrt in einem anderen gespeichert werden. Allerdings funktioniert das noch nicht so ganz. Finden Sie die Fehler.

std::unique_ptr<int[]> reverseArray(std::unique_ptr<int[]> unique_array){
    auto reversed_array = std::make_unique<int[]>(10);
    for(int i = 0; i < 10; i++){
        reversed_array[i] = 9 - unique_array[i];
    }
    
    return reversed_array;
}
{
    auto unique_array = std::make_unique<int[]>(10);
    for(int i = 0; i < 10; i++){
        unique_array[i] = i;
    }
    
    std::cout << "Das ursprüngliche Array:" << std::endl;
    for(int i = 0; i < 10; i++){
        std::cout << "Stelle: " << i << " Inhalt: " << unique_array[i] << std::endl;
    }
    
    auto reversed_array = reverseArray(unique_array);
    
    std::cout << "Das umgedrehte neue Array:" << std::endl;
    
    for(int i = 0; i < 10; i++){
        std::cout << "Stelle: " << i << " Inhalt: " << reversed_array[i] << std::endl;
    }
}

Lösungen

Lösung 1

Der Fehler befindet sich in der Ausgabezeile. Auf den Inhalt eines Pointers kann nur mit entsprechender Referenzierung mit dem * -Operator zugegriffen werden. Bei weak_ptr muss zusätzlich die Funktion lock() genutzt werden, um den Inhalt abrufen zu können. Richtig ist also std::cout << "Der Inhalt des weak-Pointers ist: " << *weak.lock() << std::endl;.

Lösung 2

unique_ptr können nicht einfach kopiert werden, da es sonst mehr als eine Referenz auf das Objekt gibt. Deswegen muss der move()-Befehl benutzt werden! Richtig ist also, sowohl bei dem Aufruf von reverseArray, als auch beim return-Statement diesen Befehl zu verwenden: auto reversed_Array = reverseArray(move(unique_array)); und return move(reversed_array);.