2013-04-24 22 views
20

Also habe ich eine bevorstehende Aufgabe, die sich mit Ausnahmen beschäftigt und sie in meinem aktuellen Adressbuchprogramm verwendet, auf das die meisten Hausaufgaben zentriert sind. Ich entschied mich dafür, mit Ausnahmen und dem ganzen Versuch zu experimentieren und ein Klassendesign zu verwenden, was ich in einigen Wochen für meinen Auftrag tun müsste. Ich habe Arbeitscode, der die Ausnahme gut überprüfen, aber was ich wissen möchte, ist, wenn es eine Möglichkeit gibt, meine Fehlermeldung Funktion zu standardisieren, (dh meine What() Anruf):Wie erstellt man Ausnahmen?

Hier ist mein Code:

Was ich gerne tun könnte, ist es so zu machen, dass meine was() -Funktion einen Wert zurückgeben kann, der davon abhängt, welcher Fehlertyp aufgerufen wird. Wenn ich zum Beispiel einen Fehler aufruft, der nach der obersten Nummer (a) aussieht, um zu sehen, ob es eine Null ist, und wenn dies der Fall wäre, würde er dann die Nachricht setzen, dass "du nicht haben kannst ein Zähler von Null ", aber immer noch in der What() -Funktion sein. Hier ein Beispiel:

virtual const char* what() const throw() 
    if(myex == 1) 
    { 
     return "You can't have a 0 for the numerator! Error code # 1 " 
    } 
    else 

    return "You can't divide by zero! Error code number 0, restarting the calculator..."; // my error message 
    } 

Das ist offensichtlich nicht funktionieren würde, aber gibt es eine Möglichkeit, es so zu machen, ich bin nicht eine andere Funktion für jede Fehlermeldung zu schreiben?

+9

Für einen Start erstellen, ruft 'main' ist rekursiv nicht legal. – jogojapan

+0

'noZero' muss (oder sollte nicht) ein Member innerhalb' testException' sein. Verschiebe es nach draußen! – SuperSaiyan

+3

Die Idee ist normalerweise, dass Sie eine separate Klasse für jeden Ausnahmetyp implementieren. Jeder von ihnen überschreibt die 'what()' -Funktion auf eine andere Art und es steht Ihnen natürlich frei, typbezogene Informationen in die zurückgegebene Zeichenfolge aufzunehmen. – jogojapan

Antwort

35

Ihr Code enthält viele Missverständnisse. Die kurze Antwort ist ja, Sie können what() ändern, um zurückzukehren, was auch immer Sie wollen. Aber lass uns Schritt für Schritt gehen.

#include <iostream> 
#include <exception> 
#include <stdexcept> 
#include <sstream> 
using namespace std; 


class DivideByZeroException: public runtime_error { 
public: 

    DivideByZeroException(int x, int y) 
    : runtime_error("division by zero"), numerator(x), denominator(y) 
    {} 

    virtual const char* what() const throw() 
    { 
    cnvt.str(""); 

    cnvt << runtime_error::what() << ": " << getNumerator() 
     << "/" << getDenominator(); 

    return cnvt.str().c_str(); 
    } 

    int getNumerator() const 
    { return numerator; } 

    int getDenominator() const 
    { return denominator; } 

    template<typename T> 
    static T divide(const T& n1, const T& n2) 
    { 
     if (n2 == T(0)) { 
      throw DivideByZeroException(n1, n2); 
     } 

     return (n1/n2); 
    } 

private: 
    int numerator; 
    int denominator; 

    static ostringstream cnvt; 
}; 

ostringstream DivideByZeroException::cnvt; 

In erster Linie runtime_error von exception abgeleitet, ist die beratung Ausnahmeklasse von abzuleiten. Dies wird im stdexcept-Header deklariert. Sie müssen den Konstruktor nur mit der Nachricht initialisieren, die Sie in der Methode what() zurückgeben möchten.

Zweitens sollten Sie Ihre Klassen entsprechend benennen. Ich verstehe, dass dies nur ein Test ist, aber ein aussagekräftiger Name wird Ihnen immer helfen, Ihren Code zu lesen und zu verstehen.

Wie Sie sehen können, habe ich den Konstruktor geändert, um die Zahlen zu akzeptieren, die die Ausnahme provoziert haben. Du hast den Test in der Ausnahme gemacht ... nun, ich habe das respektiert, aber als eine statische Funktion, die von außen aufgerufen werden kann.

Und schließlich die what() Methode. Da wir zwei Zahlen teilen, wäre es schön, zwei Zahlen zu zeigen, die die Ausnahme ausgelöst haben. Der einzige Weg, dies zu erreichen, ist die Verwendung von ostringstream. Hier machen wir es statisch, so dass es kein Problem gibt, einen Zeiger auf ein Stapelobjekt zurückzugeben (d. H. cnvt eine lokale Variable würde ein undefiniertes Verhalten einführen).

Der Rest des Programms ist es mehr oder weniger, wie Sie es in Ihrer Frage aufgeführt:

int main() 
{ 
int a, b, result; 

cout << endl; 

cout << "Enter a number to be divided " << endl; 

cout << endl; 

cin >> a; 

cout << endl; 

cout << "You entered " << a << " , Now give me a number to divide by " << endl; 

cin >> b; 

try 
{  
     result = DivideByZeroException::divide(a, b); 

    cout << "\nThe two numbers divided are " << result << endl; 
} 
catch(const DivideByZeroException &e) 
{ 
    cout << e.what() << endl; 
} 

return 0; 

} 

Wie Sie sehen können, habe ich Ihre return main() Anweisung entfernt haben. Es macht keinen Sinn, da Sie main() nicht rekursiv aufrufen können. Das Ziel ist auch ein Fehler: Sie würden erwarten, den Vorgang, der die Ausnahme ausgelöst hat, erneut zu versuchen, aber dies ist nicht möglich, da Ausnahmen nicht wiedereintrittsfähig sind.Sie können jedoch ändern, den Quellcode ein wenig, um den gleichen Effekt zu erreichen:

int main() 
{ 
int a, b, result; 
bool error; 

do { 
    error = false; 

    cout << endl; 

    cout << "Enter a number to be divided " << endl; 

    cout << endl; 

    cin >> a; 

    cout << endl; 

    cout << "You entered " << a << " , Now give me a number to divide by " << endl; 

    cin >> b; 

    try 
    {  
     result = DivideByZeroException::divide(a, b); // trys my exception from my class to see if there is an issue 

     cout << "\nThe two numbers divided are " << result << endl; 
    } 
    catch(const DivideByZeroException &e) // if the error is true, then this calls up the eror message and restarts the progrm from the start. 
    { 
     cout << e.what() << endl; 
     error = true; 
    } 
} while(error); 

return 0; 

} 

Wie Sie sehen können, im Falle eines Fehlers die Ausführung folgt, bis eine „richtige“ Teilung eingegeben wird.

Hoffe, das hilft.

+0

Es gibt wirklich keine Ausnahmeklasse, von der man "ableitet". Es ist vollkommen in Ordnung, Ihre eigenen Ausnahmeklassen zu verwenden, wenn dies Ihnen beim Programmieren hilft. Beachten Sie jedoch, dass alles in 'std' Ausnahmen von' 'auslöst. – rubenvb

+2

Es ist zwar nicht obligatorisch, aber es ist einfacher (runtime_error liefert einen Konstruktor für string, um die Hauptfehlermeldung zu setzen) und möglicherweise signifikanter als direkt von der Ausnahme abzuleiten (da Sie die "vorgeschlagene" Standardausnahmehierarchie befolgen) . Abgesehen davon, ja, Sie können sogar Int werfen, wenn Sie das vorziehen. – Baltasarq

+0

Wow. Ok, das ist ziemlich cool, danke. Zugegebenermaßen wird es eine Weile dauern, bis ich alles verstanden habe, aber das ist der Punkt, an dem ich übe. Ich denke, dass die Basisklasse, die eine Ausnahme ist, eine Voraussetzung für die Zuweisung ist, aber ich werde fragen, ob wir den runtime_error an seiner Stelle verwenden können. Unser Auftrag nach diesem deckt Vorlagen ab, also sollte die Verwendung in ihnen auch eine gute Übung sein! Vielen Dank für die Eingabe. Dies sollte Ihnen helfen zu verstehen, wie Ausnahmen funktionieren. –

2
class zeroNumerator: public std::exception 
{ 
    const char* what() const throw() { return "Numerator can't be 0.\n"; } 
}; 

//... 

try 
{ 
    myex.noZero(b); // trys my exception from my class to see if there is an issue 
    if(myex==1) 
    { 
    throw zeroNumerator(); // This would be a class that you create saying that you can't have 0 on the numerator 
    } 

} 
catch(testException &te) 
{ 
    cout << te.what() << endl; 
    return main(); 
} 

Sie sollten immer std :: exception & e verwenden. so tun

catch(std::exception & e) 
{ 
    cout<<e.what(); 
} 
+0

Wenn ich 'catch (std :: exception & e)' dann würde ich mit dem Standard-.was Ausgang –

+2

Nein stecken, weil Sie eine Klasse Klasse zeroNumerator schaffen würde: public std :: exception { \t const char *, was() const throw() {return "Numerator kann nicht 0 sein" }; –

+1

es wird den zeroNumerator fangen, weil es derselbe Versuch ist und Block fängt ... es weiß, was man fängt ... versuch es, wenn du mir nicht vertraust –

0

Sie sollten eine Hierarchie von Klassen berücksichtigen.

Der Grund dafür ist möglicherweise nicht offensichtlich, wenn Sie versuchen, Ausnahmen nur für die Übertragung einer Zeichenfolge zu verwenden, aber die eigentliche Absicht, Ausnahmen zu verwenden, sollte einen Mechanismus für die erweiterte Behandlung von Ausnahmesituationen sein. Viele Dinge werden unter der Haube der C++ - Laufzeitumgebung ausgeführt, während der Call-Stack von "throw" auf "catch" umgeleitet wird.

Ein Beispiel für die Klassen sein könnte:

für die Fehler
class DivisionError : public std::exception { 
public: 
    DevisionError(const int numerator, const int divider) 
     :numerator(numerator) 
     , divider(divider) 
    { 
    } 
    virtual const char* what() const noexcept { 
     // ... 
    } 
    int GetNumerator() const { return numerator; } 
    int GetDevider() const { return divider; } 
private: 
    const int numerator; 
    const int divider; 
}; 


class BadNumeratorError : public DivisionError { 
public: 
    BadNumeratorError(int numerator, int divider) 
     : DivisionError(numerator, divider) 
    { 
    } 
    virtual const char* what() const noexcept { 
    { 
     // ... 
    } 
}; 


class ZeroDeviderError : public DivisionError { 
public: 
    ZeroDeviderError(int numerator, int divider) 
     : DivisionError(numerator, divider) 
    { 
    } 
    virtual const char* what() const noexcept { 
    { 
     // .... 
    } 
}; 
  • Bereitstellung verschiedene Klassen, geben Sie eine Chance Entwicklern verschiedene Fehler in besonderer Weise zu behandeln (und nicht nur eine Fehlermeldung angezeigt)
  • Durch die Bereitstellung einer Basisklasse für die Fehlertypen können Entwickler flexibler sein - so spezifisch wie sie benötigen.

In einigen Fällen müssen sie

} catch (const ZeroDividerError & ex) { 
// ... 
} catch (const DivisionError & ex) { 

in anderen spezifisch sein, nicht

} catch (const DivisionError & ex) { 

Wie für einige weitere Details,

  • Sie sollten nicht Erstellen Sie Objekte Ihrer Ausnahmen, bevor Sie auf die Art und Weise werfen, wie Sie es getan haben. Unabhängig von Ihrer Absicht, es ist nur nutzlos - sowieso, Sie arbeiten mit einer Kopie des Objekts im Catch-Bereich (nicht durch Zugang über Referenz verwechselt werden)
  • Mit einer const-Referenz wäre ein guter Stil catch (const testException &te), wenn Sie wirklich brauche ein nicht konstantes Objekt.
2

Sie können Ihre eigene Exception-Klasse für Längenfehler wie diese

class MyException : public std::length_error{ 
public: 
    MyException(const int &n):std::length_error(to_string(n)){} 
};