Im Notebook zu Exceptions wurde das Konzept von Exceptions in C++ erläutert: Diese dienen dazu, bei Ausnahmesituationen den normalen Programmablauf zu unterbrechen und optional eine Fehlerbehandlung zu starten.
Dafür muss zunächst definiert werden, welche Art von Fehlermeldung
geworfen werden soll. Hierfür ist es möglich, von
std::exception erbende Klassen zu erstellen, es existieren
aber auch schon einige standardmäßig in C++. Daraufhin wird im
Abschnitt, bei dem ein Fehler auftreten könnte, mittels
throw eine Instanz jener Exception erzeugt und an die
aufrufende Funktion übergeben. Bei vielen bereits vorhandenen Klassen
wie beispielsweise den elementaren Datentypen sind diese Maßnahmen schon
implementiert, sodass nur noch auf die korrekte Behandlung dieser
eventuell auftretenden Exceptions geachtet werden muss.
Dafür wird, falls vorhanden, der den Funktionsaufruf umschließende
catch-Block mit dem passenden Typ einer Exception
aufgerufen. Wenn der Fehler behebbar ist, sollten entsprechende Schritte
eingeleitet werden, andernfalls ist oft eine Ausgabe der Fehlermeldung
mit what hilfreich. Existiert kein passendes
catch, bricht die Ausführung des Programms ebenfalls mit
der Ausgabe einer Fehlermeldung ab.
Unten finden Sie die Codebasis auf dem Stand der letzten Übung zu
Containerklassen, die nun am Ende erweitert wird. Es soll eine Exception
geworfen werden, falls zwischen gewünschtem Start und Ziel keine
Verbindung besteht. In diesem Fall wird aktuell in
Train::vGoTo(...) eine einfache Fehlermeldung über die
Konsole ausgegeben. Diese Aufgabe soll im Folgenden eine Exception
übernehmen:
TrainException KlasseUm das oben beschriebene Verhalten zu erreichen, soll die Klasse
TrainException erstellt werden, welche von
std::exception erbt. Zunächst müssen dafür die beiden
Libraries <exception> und
<stdlib.h> inkludiert werden. Außerdem verfügt diese
Klasse als Membervariable über einen Shared Pointer auf den Bahnhof, der
angefahren werden soll, um im weiteren Verlauf dessen Namen
auszugeben.
Ansonsten wenden Sie dieselben Konzepte an, die Sie bisher beim Erstellen von Klassen und Vererbungsstrukturen erlernt haben; insbesondere also Konstruktor, Destruktor und passende Zugriffsspezifizierer.
Zur Realisierung der Ausgabe implementieren Sie die Methode
TrainException::vExplain(), die auf der Konsole ausgibt,
welchen Bahnhof der Zug anfahren wollte und dass das nicht möglich ist,
da keine Verbindung existiert
Werfen Sie nun anstelle der einfachen Fehlermeldung im
else-Zweig von Train::vGoTo(...) eine
TrainException, der der Shared Pointer im Konstruktor
übergeben wird. Führen Sie nun die Testaufrufe innnerhalb eines
try-catch Blockes durch, der im Fehlerfall
TrainException::vExplain() der geworfenen Exception
aufruft.
#include <iostream>
#include <string>
#include <memory>
#include <vector>
class Station
{
private:
std::string p_sName;
std::vector<std::shared_ptr<Station>> p_pNeighbors;
public:
Station(std::string station);
void vAddNeighbor(std::shared_ptr<Station> neighbor);
std::string getName() const;
void vPrintNeighbors() const;
bool isNeighbor(std::shared_ptr<Station> request) const;
};
Station::Station(std::string station) : p_sName(station)
{}
// falls request ein bekannter Nachbar ist: returnt 1, sonst 0
bool Station::isNeighbor(std::shared_ptr<Station> request) const
{
// falls request in p_pNeighbors:
if(std::find(p_pNeighbors.begin(), p_pNeighbors.end(), request) != p_pNeighbors.end()) {
return 1;
}
// sonst:
return 0;
}
void Station::vPrintNeighbors() const
{
std::cout << std::endl << "Folgende Bahnhoefe koennen von " << p_sName << " aus angefahren werden: ";
for (auto n : p_pNeighbors) {
std::cout << n->getName() << ", ";
}
}
std::string Station::getName() const
{
return p_sName;
}
void Station::vAddNeighbor(std::shared_ptr<Station> neighbor)
{
p_pNeighbors.push_back(neighbor);
}
class Train
{
private:
static inline int p_iIDCounter = 0;
const int p_iID = p_iIDCounter++;
std::shared_ptr<Station> p_pIsAt = nullptr;
protected:
float p_fDelay = 0;
public:
Train() = default;
virtual ~Train() = 0;
virtual void vPrintProperties(std::ostream& ausgabe) const;
void vGoTo(std::shared_ptr<Station> to);
};
Train::~Train()
{
std::cout << "Aufruf des rein virtuellen Destruktors" << std::endl;
}
void Train::vPrintProperties(std::ostream& ausgabe) const
{
ausgabe << "ID: " << p_iID << std::endl;
//ausgabe << "Anzahl Passagiere: " << p_iPassengerCounter << std::endl; -> jetzt in PassengerTrain
ausgabe << "momentane Verspaetung: " << p_fDelay << " Minuten"<< std::endl;
}
class PassengerTrain : public Train
{
public:
PassengerTrain() = default;
PassengerTrain(int p_iPassengerCounter);
virtual ~PassengerTrain() = default;
void vPrintProperties(std::ostream& ausgabe) const override;
float fStation(int passengersIn, int passengersOut);
private:
int p_iPassengerCounter = 0;
};
PassengerTrain::PassengerTrain(int passengers) : Train(), p_iPassengerCounter(passengers)
{
std::cout << "Aufruf des Nicht-Standardkonstruktors" << std::endl;
}
void PassengerTrain::vPrintProperties(std::ostream& ausgabe) const
{
Train::vPrintProperties(ausgabe);
ausgabe << "Anzahl Passagiere: " << p_iPassengerCounter << std::endl;
}
// gibt Änderung der Verspätung zurück und Verändert `delay` (aber delay >= 0)
/* Annahmen / Funktion
bei Haltestelle: 2 Minuten Zeit.
Pro einsteigender Passagier: 10 Sekunden
Pro aussteigender Passagier: 5 Sekunden
nicht parallel
alles >2 Minuten: neue Verspätung
*/
float PassengerTrain::fStation(int passengersIn, int passengersOut)
{
// mehr Passagiere als Aussteigende? -> fehlerhafte Einhabe -> mache nichts
if (p_iPassengerCounter < passengersOut)
{
std::cout << "Es koennen nicht mehr Personen aussteigen als sich im Zug befinden" << std::endl;
return 0;
}
p_iPassengerCounter = p_iPassengerCounter + passengersIn - passengersOut;
// Rechnung in Sekunden, Umrechnung in Minuten spaeter
float secondsChange = 120 - passengersIn * 10 - passengersOut * 5;
p_fDelay = (p_fDelay - secondsChange < 0) ? 0 : (p_fDelay - secondsChange)/60;
return secondsChange/60;
}
class FreightTrain : public Train
{
private:
float p_fChargeQuantity = 0.0;
std::string p_sChargeType = "default";
public:
FreightTrain() = default;
FreightTrain(float chargeQuantity, std::string chargeType);
//void vPrintProperties() const;
void vPrintProperties(std::ostream& ausgabe) const override;
void vLoadCharge(float amount);
};
FreightTrain::FreightTrain(float chargeQuantity, std::string chargeType) : Train(), p_fChargeQuantity(chargeQuantity), p_sChargeType(chargeType)
{}
void FreightTrain::vPrintProperties(std::ostream& ausgabe) const
{
Train::vPrintProperties(ausgabe);
ausgabe << "Ladung: " << p_fChargeQuantity << " " << p_sChargeType << std::endl;
}
// abladen: amount < 0
void FreightTrain::vLoadCharge(float amount)
{
if (p_fChargeQuantity + amount < 0)
{
std::cout << "Es kann nicht mehr abgeladen werden als geladen ist. Abbruch." << std::endl;
return;
}
p_fChargeQuantity += amount;
std::cout << "Es wurden " << amount << " " << p_sChargeType;
if (amount > 0)
{
std::cout << " aufgeladen.";
}
else
{
std::cout << " abgeladen.";
}
std::cout << " Es befinden sich nun " << p_fChargeQuantity << " " << p_sChargeType << " auf dem Zug." << std::endl;
}
#define OPERATOR operator<<
std::ostream & OPERATOR(std::ostream& out, const Train& train)
{
train.vPrintProperties(out);
return out;
}
#undef OPERATOR
//TrainException.h
//hier: Klasse TrainException implementieren
//TrainException.cpp
//hier: Konstruktor implementieren
//TrainException.cpp
//hier: Destruktor implementieren
//TrainException.cpp
//hier: vExplain(...) implementieren
void Train::vGoTo(std::shared_ptr<Station> to)
{
if((p_pIsAt==nullptr) || (p_pIsAt->isNeighbor(to)))
{
p_pIsAt = to;
std::cout << "Zug ist nach " << p_pIsAt->getName() << " gefahren." << std::endl;
}
else
{
std::cout << "Der Zug kann nicht nach " << to->getName() << " fahren: Es gibt keine Verbindung." << std::endl;
}
/*
// Fahre nur, wenn Zug im Anfangszustand, oder das Ziel und die aktuelle Haltestelle verbunden sind:
if((p_sIsAt==nullptr) || (p_sIsAt->getDestination() == to))
{
p_sIsAt = to;
std::cout << "Zug ist nach " << p_sIsAt->getName() << " gefahren." << std::endl;
}
else
{
std::cout << "Der Zug kann nicht nach " << to->getName() << " fahren: Es gibt keine Verbindung." << std::endl;
}
*/
}
// 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;
// alter Test: gleiches Verhalten wie in 04_smartpointer
/*
auto berlin = std::make_shared<Station>("Berlin");
auto hamburg = std::make_shared<Station>("Hamburg");
auto frankfurt = std::make_shared<Station>("Frankfurt");
berlin->vAddNeighbor(hamburg);
hamburg->vAddNeighbor(berlin);
aTrain->vGoTo(berlin);
aTrain->vGoTo(hamburg);
aTrain->vGoTo(frankfurt);
*/
// neu: Strecken"netz"
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>("Koeln");
auto muenchen = std::make_shared<Station>("Muenchen");
berlin->vAddNeighbor(hamburg);
berlin->vAddNeighbor(frankfurt);
hamburg->vAddNeighbor(berlin);
hamburg->vAddNeighbor(koeln);
// Strecke nur in eine Richtung
koeln->vAddNeighbor(frankfurt);
frankfurt->vAddNeighbor(koeln);
frankfurt->vAddNeighbor(berlin);
frankfurt->vAddNeighbor(muenchen);
muenchen->vAddNeighbor(frankfurt);
// Zeige Liste der bekannten Nachbarn
berlin->vPrintNeighbors();
hamburg->vPrintNeighbors();
koeln->vPrintNeighbors();
frankfurt->vPrintNeighbors();
muenchen->vPrintNeighbors();
std::cout<< "\n\n\nZuege fahren lassen: \n\n";
aTrain->vGoTo(berlin);
aTrain->vGoTo(hamburg);
aTrain->vGoTo(koeln);
aTrain->vGoTo(hamburg); // Fehlermeldung: keine Verbindung
Eigenschaften 'aTrain':
ID: 0
momentane Verspaetung: 0 Minuten
Anzahl Passagiere: 0
Eigenschaften 'bTrain':
ID: 1
momentane Verspaetung: 0 Minuten
Ladung: 2 tons of wood
Folgende Bahnhoefe koennen von Berlin aus angefahren werden: Hamburg, Frankfurt,
Folgende Bahnhoefe koennen von Hamburg aus angefahren werden: Berlin, Koeln,
Folgende Bahnhoefe koennen von Koeln aus angefahren werden: Frankfurt,
Folgende Bahnhoefe koennen von Frankfurt aus angefahren werden: Koeln, Berlin, Muenchen,
Folgende Bahnhoefe koennen von Muenchen aus angefahren werden: Frankfurt,
Zuege fahren lassen:
Zug ist nach Berlin gefahren.
Zug ist nach Hamburg gefahren.
Zug ist nach Koeln gefahren.
Der Zug kann nicht nach Hamburg fahren: Es gibt keine Verbindung.
#include <iostream>
#include <string>
#include <memory>
#include <vector>
#include <exception>
#include <stdlib.h>
class Station
{
private:
std::string p_sName;
std::vector<std::shared_ptr<Station>> p_pNeighbors;
public:
Station(std::string station);
virtual ~Station() = default;
void vAddNeighbor(std::shared_ptr<Station> neighbor);
std::string getName() const;
void vPrintNeighbors() const;
bool isNeighbor(std::shared_ptr<Station> request) const;
};
Station::Station(std::string station) : p_sName(station)
{}
// falls request ein bekannter Nachbar ist: returnt 1, sonst 0
bool Station::isNeighbor(std::shared_ptr<Station> request) const
{
// falls request in p_pNeighbors:
if(std::find(p_pNeighbors.begin(), p_pNeighbors.end(), request) != p_pNeighbors.end()) {
return 1;
}
// sonst:
return 0;
}
void Station::vPrintNeighbors() const
{
std::cout << std::endl << "Folgende Bahnhoefe koennen von " << p_sName << " aus angefahren werden: ";
for (auto n : p_pNeighbors) {
std::cout << n->getName() << ", ";
}
}
std::string Station::getName() const
{
return p_sName;
}
void Station::vAddNeighbor(std::shared_ptr<Station> neighbor)
{
p_pNeighbors.push_back(neighbor);
}
class Train
{
private:
static inline int p_iIDCounter = 0;
const int p_iID = p_iIDCounter++;
std::shared_ptr<Station> p_pIsAt = nullptr;
protected:
float p_fDelay = 0;
public:
Train() = default;
virtual ~Train() = 0;
virtual void vPrintProperties(std::ostream& ausgabe) const;
void vGoTo(std::shared_ptr<Station> to);
std::shared_ptr<Station> getIsAt();
};
std::shared_ptr<Station> Train::getIsAt()
{
return p_pIsAt;
}
Train::~Train()
{
std::cout << "Aufruf des rein virtuellen Destruktors" << std::endl;
}
void Train::vPrintProperties(std::ostream& ausgabe) const
{
ausgabe << "ID: " << p_iID << std::endl;
//ausgabe << "Anzahl Passagiere: " << p_iPassengerCounter << std::endl; -> jetzt in PassengerTrain
ausgabe << "momentane Verspaetung: " << p_fDelay << " Minuten"<< std::endl;
}
class PassengerTrain : public Train
{
public:
PassengerTrain() = default;
PassengerTrain(int p_iPassengerCounter);
virtual ~PassengerTrain() = default;
void vPrintProperties(std::ostream& ausgabe) const override;
float fStation(int passengersIn, int passengersOut);
private:
int p_iPassengerCounter = 0;
};
PassengerTrain::PassengerTrain(int passengers) : Train(), p_iPassengerCounter(passengers)
{
std::cout << "Aufruf des Nicht-Standardkonstruktors" << std::endl;
}
void PassengerTrain::vPrintProperties(std::ostream& ausgabe) const
{
Train::vPrintProperties(ausgabe);
ausgabe << "Anzahl Passagiere: " << p_iPassengerCounter << std::endl;
}
// gibt Änderung der Verspätung zurück und Verändert `delay` (aber delay >= 0)
/* Annahmen / Funktion
bei Haltestelle: 2 Minuten Zeit.
Pro einsteigender Passagier: 10 Sekunden
Pro aussteigender Passagier: 5 Sekunden
nicht parallel
alles >2 Minuten: neue Verspätung
*/
float PassengerTrain::fStation(int passengersIn, int passengersOut)
{
// mehr Passagiere als Aussteigende? -> fehlerhafte Einhabe -> mache nichts
if (p_iPassengerCounter < passengersOut)
{
std::cout << "Es koennen nicht mehr Personen aussteigen als sich im Zug befinden" << std::endl;
return 0;
}
p_iPassengerCounter = p_iPassengerCounter + passengersIn - passengersOut;
// Rechnung in Sekunden, Umrechnung in Minuten spaeter
float secondsChange = 120 - passengersIn * 10 - passengersOut * 5;
p_fDelay = (p_fDelay - secondsChange < 0) ? 0 : (p_fDelay - secondsChange)/60;
return secondsChange/60;
}
class FreightTrain : public Train
{
private:
float p_fChargeQuantity = 0.0;
std::string p_sChargeType = "default";
public:
FreightTrain() = default;
FreightTrain(float chargeQuantity, std::string chargeType);
//void vPrintProperties() const;
void vPrintProperties(std::ostream& ausgabe) const override;
void vLoadCharge(float amount);
};
FreightTrain::FreightTrain(float chargeQuantity, std::string chargeType) : Train(), p_fChargeQuantity(chargeQuantity), p_sChargeType(chargeType)
{}
void FreightTrain::vPrintProperties(std::ostream& ausgabe) const
{
Train::vPrintProperties(ausgabe);
ausgabe << "Ladung: " << p_fChargeQuantity << " " << p_sChargeType << std::endl;
}
// abladen: amount < 0
void FreightTrain::vLoadCharge(float amount)
{
if (p_fChargeQuantity + amount < 0)
{
std::cout << "Es kann nicht mehr abgeladen werden als geladen ist. Abbruch." << std::endl;
return;
}
p_fChargeQuantity += amount;
std::cout << "Es wurden " << amount << " " << p_sChargeType;
if (amount > 0)
{
std::cout << " aufgeladen.";
}
else
{
std::cout << " abgeladen.";
}
std::cout << " Es befinden sich nun " << p_fChargeQuantity << " " << p_sChargeType << " auf dem Zug." << std::endl;
}
#define OPERATOR operator<<
std::ostream & OPERATOR(std::ostream& out, const Train& train)
{
train.vPrintProperties(out);
return out;
}
#undef OPERATOR
class TrainException : std::exception
{
private:
std::shared_ptr<Station> p_pDestination;
public:
TrainException(std::shared_ptr<Station> destination);
virtual ~TrainException();
void vExplain(); // ganz unten
};
TrainException::TrainException(std::shared_ptr<Station> destination) : p_pDestination(destination)
{}
TrainException::~TrainException()
{}
void TrainException::vExplain()
{
std::cout << "Der Zug versuchte nach " << p_pDestination->getName() << " zu fahren. Nicht moeglich, da keine Verbindung vorhanden. Ende." << std::endl;
}
void Train::vGoTo(std::shared_ptr<Station> to)
{
if((p_pIsAt==nullptr) || (p_pIsAt->isNeighbor(to)))
{
p_pIsAt = to;
std::cout << "Zug ist nach " << p_pIsAt->getName() << " gefahren." << std::endl;
}
else
{
throw TrainException(to);
std::cout << "Der Zug kann nicht nach " << to->getName() << " fahren: Es gibt keine Verbindung." << std::endl;
}
}
// Test
std::shared_ptr<PassengerTrain> aTrain;
aTrain = std::make_shared<PassengerTrain>();
auto bTrain = std::make_shared<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;
// neu: Strecken"netz"
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>("Koeln");
auto muenchen = std::make_shared<Station>("Muenchen");
berlin->vAddNeighbor(hamburg);
berlin->vAddNeighbor(frankfurt);
hamburg->vAddNeighbor(berlin);
hamburg->vAddNeighbor(koeln);
// Strecke nur in eine Richtung
koeln->vAddNeighbor(frankfurt);
frankfurt->vAddNeighbor(koeln);
frankfurt->vAddNeighbor(berlin);
frankfurt->vAddNeighbor(muenchen);
muenchen->vAddNeighbor(frankfurt);
// Zeige Liste der bekannten Nachbarn
berlin->vPrintNeighbors();
hamburg->vPrintNeighbors();
koeln->vPrintNeighbors();
frankfurt->vPrintNeighbors();
muenchen->vPrintNeighbors();
Eigenschaften 'aTrain':
ID: 0
momentane Verspaetung: 0 Minuten
Anzahl Passagiere: 0
Eigenschaften 'bTrain':
ID: 1
momentane Verspaetung: 0 Minuten
Ladung: 2 tons of wood
Folgende Bahnhoefe koennen von Berlin aus angefahren werden: Hamburg, Frankfurt,
Folgende Bahnhoefe koennen von Hamburg aus angefahren werden: Berlin, Koeln,
Folgende Bahnhoefe koennen von Koeln aus angefahren werden: Frankfurt,
Folgende Bahnhoefe koennen von Frankfurt aus angefahren werden: Koeln, Berlin, Muenchen,
Folgende Bahnhoefe koennen von Muenchen aus angefahren werden: Frankfurt,
try
{
std::cout<< "\n\n\nZuege fahren lassen: \n\n";
aTrain->vGoTo(berlin);
aTrain->vGoTo(hamburg);
aTrain->vGoTo(koeln);
aTrain->vGoTo(hamburg); // Fehlermeldung: keine Verbindung
}
catch(TrainException &e)
{
e.vExplain();
}
Zuege fahren lassen:
Zug ist nach Berlin gefahren.
Zug ist nach Hamburg gefahren.
Zug ist nach Koeln gefahren.
Error:
Warum wird mit der Implementierung von Train::vGoTo() im
Lösungsvorschlag bei einem fehlerhaften Aufruf der Inhalt der Exception
ausgegeben, nicht aber die normale Ausgabe in der Zeile darunter (die in
der vorherigen Implementierung ausgeführt wurde)?
Welches Verhalten erwarten Sie von den unten aufgeführten
Methodenaufrufen? Wie könnte erreicht werden, dass auch bei einzelnen
fehlerhaften Aufrufen von Train::vGoTo() die darauf
folgenden Anweisungen ausgeführt werden?
try
{
std::cout<< "\n\n\nZuege fahren lassen: \n\n";
aTrain->vGoTo(berlin);
aTrain->vGoTo(hamburg);
aTrain->vGoTo(frankfurt);
aTrain->vGoTo(koeln);
aTrain->vGoTo(frankfurt);
aTrain->vGoTo(hamburg);
aTrain->vGoTo(berlin);
}
catch(TrainException &e)
{
e.vExplain();
}
Nach dem Werfen einer Exception mittels throw wird die
fehlerhafte Funktion verlassen, bis die Exception durch ein passendes
catch abgefangen wird. Bei einander aufrufenden Funktionen
geschieht das so lange, wie die Exception unbehandelt bleibt, d.h. es
können durch eine Exception auch mehrere Funktionen verlassen werden.
Wenn schließlich die eigentliche main-Methode des Programms
verlassen werden soll, bricht die Ausführung des Programms mit der
Ausgabe dieser Exception ab.
Da keine direkte Verbindung von Hamburg nach Frankfurt besteht, würde
die Ausführung bei der aktuellen Implementierung an dieser Stelle
abbrechen, da eine Exception geworfen und der catch-Block
ausgeführt wird. Um dieses Verhalten zu verhindern, könnte jeder Aufruf
von Train::vGoTo() in einem separaten
try-catch Block stattfinden, was allerdings schnell
unübersichtlich wird:
std::cout<< "\n\n\nZuege fahren lassen: \n\n";
try{aTrain->vGoTo(berlin); }
catch(TrainException &e) { e.vExplain();}
try{aTrain->vGoTo(hamburg); }
catch(TrainException &e) { e.vExplain();}
try{aTrain->vGoTo(frankfurt); }
catch(TrainException &e) { e.vExplain();}
try{aTrain->vGoTo(koeln); }
catch(TrainException &e) { e.vExplain();}
try{aTrain->vGoTo(frankfurt); }
catch(TrainException &e) { e.vExplain();}
try{aTrain->vGoTo(hamburg); }
catch(TrainException &e) { e.vExplain();}
try{aTrain->vGoTo(berlin); }
catch(TrainException &e) { e.vExplain();}