online compiler and debugger for c/c++

code. compile. run. debug. share.
Source Code   
Language
#include <algorithm> #include <fstream> #include <iostream> #include <map> #include <stdexcept> #include <string> #include <sstream> using namespace std; #include "Airport.h" #include "Connection.h" static const char *const help = R"( Use one of the following (which means) SETUP ------------------------------------------------------- + airport ... (add given airport(s)) - airport ... (remove given airport(s)) : flight [airport airport ...] (define connection) QUERY ------------------------------------------------------- < [select] (list outgoing flights from matching airport(s)) > [select] (list incomming flights to matching airport(s)) = [select] (list route of matching flight(s)) OTHER ------------------------------------------------------- $ filename (read commands from filename) ? (show this help page) ! (check memory usage) . (terminate) )"; class ParseException : virtual public runtime_error { public: ParseException(const string& reason) : runtime_error("ERROR " + reason) {} }; bool parse(const string& line, const std::string& incfile = std::string()) { static map<string, shared_ptr<Connection>>knownConnections; static map<string, shared_ptr<Airport>> knownAirports; istringstream is(line + " "); char cmdc; if (!(is >> cmdc).good()) return false; if (!incfile.empty()) cout << '$' << incfile << ' ' << line << std::endl; string airportName; string flightNumber; string includeFile; switch (cmdc) { case '+': while (is >> airportName) { if (knownAirports.count(airportName)) { cout << airportName << " already in database" << endl; return true; } knownAirports.insert(make_pair(airportName, make_shared<Airport>(airportName))); cout << "added airport " << airportName << " to database" << endl; } return true; case '-': while (is >> airportName) { knownAirports.erase(airportName); cout << "removed airport " << airportName << " from database" << endl; } return true; case ':': if (is >> flightNumber) { const auto existingConnection = knownConnections.find(flightNumber); const bool isKnownConnection = (existingConnection != knownConnections.end()); if (isKnownConnection) { for (const auto e : knownAirports) { e.second->removeConnection(existingConnection->second); } knownConnections.erase(flightNumber); } vector<shared_ptr<Airport>> visitedAirports; while (is >> airportName) { auto foundAirport = knownAirports.find(airportName); if (foundAirport == knownAirports.end()) { throw ParseException("no such airport: " + airportName); } if (find(visitedAirports.begin(), visitedAirports.end(), foundAirport->second) != visitedAirports.end()) { throw ParseException("duplicate airport:" + airportName); return true; } visitedAirports.push_back(foundAirport->second); } if (visitedAirports.size() < 2) { knownConnections.erase(flightNumber); if (!visitedAirports.empty()) throw ParseException("too few airports:" + visitedAirports.front()->getName()); if (isKnownConnection) { cout << "removed connection " << flightNumber << " from database" << endl; } } else { const auto currentConnection = make_shared<Connection>(flightNumber); knownConnections.insert(make_pair(flightNumber, currentConnection)); cout << (isKnownConnection ? "modifying" : "creating") << " connection " << flightNumber << " in database" << endl; for (const auto currentAirport : visitedAirports) { const auto currentConnection = knownConnections[flightNumber]; const auto nthStop = currentConnection->addAirport(currentAirport); currentAirport->addConnection(currentConnection, nthStop); cout << "added airport " << currentAirport->getName() << " to connection " << currentConnection->getFlight() << endl; } } } return true; case '<': case '>': if (!(is >> airportName >> ws).eof()) airportName.clear(); for (const auto e : knownAirports) { if (airportName != "*" && e.first.find(airportName) == string::npos) continue; cout << e.first << endl; for (const auto c : e.second->getConnections()) { const auto &li = (cmdc == '>') ? get<1>(c)->getComingFrom(get<0>(c)) : get<1>(c)->getGoingTo(get<0>(c)) ; if (!li.empty()) { cout << ' ' << cmdc << ' ' << get<1>(c)->getFlight() << ':'; for (const auto a : li) { cout << ' ' << a->getName(); } cout << endl; } } } return true; case '=': if (!(is >> flightNumber >> ws).eof()) flightNumber.clear(); for (const auto e : knownConnections) { if (flightNumber != "*" && e.first.find(flightNumber) == string::npos) continue; cout << e.first << " ="; for (const auto a : e.second->getAirports()) cout << ' ' << a->getName(); cout << endl; } return true; case '!': cout << Airport::instances << " airport(s), " << Connection::instances << " flight(s)" << endl; return true; case '$': if (!(is >> includeFile)) throw ParseException("file name missing"); else { ifstream ifs(includeFile); if (!ifs.good()) throw ParseException("cannot open file: " + includeFile); string line; while (getline(ifs, line)) parse(line, includeFile + "$" + incfile); } return true; case '#': return true; default: cout << "unknown command: " << line << endl; // FALLTHROUGH case '?': if (!incfile.empty()) return false; cout << help << endl; return true; case '.': if (!incfile.empty()) return true; cout << "bye, bye" << endl; return false; } } int main() { string line; while (getline(cin, line)) { try { if (!parse(line)) break; } catch (const ParseException &ex) { cout << ex.what() << endl; } } }
Die etwas längere Übung für den Abschluss des Tages =================================================== Bei dieser Aufgabe müssen Sie sich zunächst etwas Zeit nehmen um die Quelltexte des `airtravel`-Projekts näher anzusehen. Dieses besteht aus den wesentlichen zwei Klassen `Airport` und `Connection`, welche jeweils Flughäfen und Flugverbindungen definieren. Da die Flughäfen (über `std::shared_ptr`) auf die Flugverbindungen verweisen, während diese wiederum (über `std::shared_ptr` auf die Flughäfen (zurück) verweisen, besteht hier die prinzipielle Gefahr zyklischer Referenzen, welche trotz der Verwendung referenzzählender Zeigern letzten Endes einen Memory-Leak verursachen könnten. Wenn Sie das Programm `./airtravel` gestarten haben, erhalten Sie mit `?` eine Übersicht über die verfügbaren Kommandos, mit denen Flughäfen und -verbindungen angelegt, aufgelistet und wieder gelöscht werden können. Spielen Sie alle diese Möglichkeiten durch, um damit etwas vertraut zu werden. Damit Sie nicht immer den Datenbestand händisch wieder neu aufbauen müssen, ist die Datei `setup` vorbeitet, die Sie mit dem `$`-Kommando einlesen können. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 1. Teil: Demonstrieren Sie zwei Szenarien, die jeweils damit beginnen, dass Sie das in der Datei `setup` vorbereitete Szenario einlesen und anschließend einige Teile davon wieder per Hand entfernen. Je nachdem wie Sie dann weiter vorgehen, werden am Ende weder Flughafen-Objekte noch Flugverbindungs-Objekte übrig bleiben ODER aber es gibt noch einige solcher Objekte, die sich aber nicht mehr mit den Kommandos '>', '<' und '=' auflisten lassen. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 2. Teil: Bereiten Sie die Beseitigung des Memory-Leaks vor, indem Sie zunächst für die expliziten `std::shared_ptr<Airport>` und `std::shared_ptr<Connection>` jeweils einen Typ-Alias `AirportRef` und `ConnectionRef` einführen. Ziel: Das Programm muss nach dieser Änderung fehlerfrei kompilierbar sein und es wird natürlich weiterhin das Problem mit dem Memory-Leak haben. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 3. Teil: Ändern Sie nun mindestens EINEN der beiden Typ-Aliase von `std::shared_ptr` in `std::weak_ptr`, AUSGENOMMEN in `airtravel.cpp`, dort MÜSSEN `knownAirports` und `knownConnections` weiterhin via `std::shared_ptr` auf `Airport`- bzw. `Connection`-Objekte referenzieren, damit diese (mindestens) so lange am Leben gehalten werden, wie sie dort bekannt sind. NATÜRLICH WIRD ES JETZT FEHLER GEBEN. Versuchen Sie diese zunächst mit dem minimal möglichen Aufwand zu beseitigen, auch wenn das bedeutet, dass bei gelöschten Flughäfen oder Verbindungen in den Übersichten nicht mehr alles in der früheren Form ausgegeben wird. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 4. Teil: Da die im letzten Schritt ersetzten `std::shared_ptr` natürlich nicht grundlos existierten, sollten Sie nun auch auch eine Lösung für den Fall finden, dass aus dem `std::weak_ptr` u.U. kein `std::shared_ptr` mehr gemacht werden kann, weil das ursprünglich referenzierte Objekt mittlerweile gelöscht wurde! Die Weiterführung der Aufgabe hängt nun davon ab, welchen der `std::shared_ptr` Sie zu einem `std::weak_ptr` gemacht haben: * Wenn aus der Sicht der Flugverbindungen nun die Flughäfen nur noch über `std::weak_ptr` erreichbar sind, sollte in der Auflistung der angeflogenen Stationen auch bei einem zwischenzeitlich gelöschten Flughafen weiterhin dessen Name erscheinen, aber mit dem nachgestellten Zusatz `[closed]`. * Wenn dagegen aus der Sicht der Flughäfen nun die Flugverbindungen nur noch über `std::weak_ptr` erreichbar sind, sollte bei einer weggefallenen Flungverbindung diese stillschweigend aus der Übersicht der ankommenden oder abgehenden Verbungen dieses Flughafens entfernt werden – UND natürlich auch aus der Menge der `std::weak_ptr`, das dort gehalten wird.
#ifndef AIRPORT_H_ #define AIRPORT_H_ #include <list> using std::list; #include <memory> using std::shared_ptr; #include <string> using std::string; #include <tuple> using std::tuple; class Connection; class Airport { const string name; list<tuple<size_t, shared_ptr<Connection>>> connections; public: Airport(const string &name_) : name(name_) { ++instances; } ~Airport() { --instances; } string getName() const { return name; } void addConnection(shared_ptr<Connection> c, size_t n); void removeConnection(shared_ptr<Connection> c); const list<tuple<size_t, shared_ptr<Connection>>> &getConnections() const; static size_t instances; }; #endif /* AIRPORT_H_ */
#include "Airport.h" #include "Connection.h" using namespace std; void Airport::addConnection(shared_ptr<Connection> c, size_t n) { removeConnection(c); connections.emplace_back(n, c); } void Airport::removeConnection(shared_ptr<Connection> c) { connections.remove_if( [c](const tuple<size_t, shared_ptr<Connection>> &_1) { return get<1>(_1) == c; } ); } const list<tuple<size_t, shared_ptr<Connection>>> &Airport::getConnections() const { return connections; } size_t Airport::instances;
#ifndef CONNECTION_H_ #define CONNECTION_H_ #include <memory> using std::shared_ptr; #include <string> using std::string; #include <vector> using std::vector; class Airport; class Connection { const string flight; vector<shared_ptr<Airport>> airports; public: Connection(const string &flight_) : flight(flight_) { ++instances; } ~Connection() { --instances; } string getFlight() const { return flight; } const vector<shared_ptr<Airport>> &getAirports() const { return airports; } size_t addAirport(shared_ptr<Airport> ap); vector<shared_ptr<Airport>> getComingFrom(size_t to) const; vector<shared_ptr<Airport>> getGoingTo(size_t from) const; static size_t instances; }; #endif /* CONNECTION_H_ */
#include "Airport.h" #include "Connection.h" using namespace std; size_t Connection::addAirport(shared_ptr<Airport> ap) { const auto result = airports.size(); airports.push_back(ap); return result; } vector<shared_ptr<Airport>> Connection::getGoingTo(size_t from) const { vector<shared_ptr<Airport>> result; while (++from < airports.size()) result.push_back(airports.at(from)); return result; } vector<shared_ptr<Airport>> Connection::getComingFrom(size_t to) const { vector<shared_ptr<Airport>> result; for (size_t i = 0; i < to; ++i) result.push_back(airports.at(i)); return result; } size_t Connection::instances;

Compiling Program...

Command line arguments:
Standard Input: Interactive Console Text
×

                

                

Program is not being debugged. Click "Debug" button to start program in debug mode.

#FunctionFile:Line
VariableValue
RegisterValue
ExpressionValue