C++-Ausnahmebehandlung beherrschen: Alles, was Sie wissen müssen

1. Grundlegende Syntax für die Ausnahmebehandlung

C++ stellt drei Schlüsselwörter für Ausnahmen bereit: try{ throw } catch{ }.

Fügen Sie hier eine Bildbeschreibung ein

#include <stdexcept>
#include <limits>
#include <iostream>
using namespace std;
void MyFunc(int c)
{
    
    
	if (c > numeric_limits< char> ::max())
		throw invalid_argument("throw MyFunc argument too large.");
	//...
}
int main()
{
    
    
	try
	{
    
    
		MyFunc(256); //cause an exception to throw
	}
	catch (invalid_argument& e)
	{
    
    
		cerr << "catch " << e.what() << endl;
		return -1;
	}
	//...
	return 0;
}

tryWenn innerhalb eines Blocks eine Ausnahme ausgelöst wird, wird diese catchvom ersten zugehörigen Block abgefangen, dessen Typ mit der Ausnahme übereinstimmt. Mit anderen Worten: Die Ausführung throwspringt von Anweisung zu catchAnweisung. Wenn kein verfügbarer catchBlock gefunden wird, std::terminatewird das Programm aufgerufen und beendet. std::exceptionIn C++ kann jeder Typ ausgelöst werden. Es wird jedoch empfohlen , Typen auszulösen , die direkt oder indirekt von abgeleitet sind. Im obigen Beispiel ist der Ausnahmetyp in der Standardbibliothek in der Header-Datei definiert . invalid_argument<stdexcept>

Die Syntax ist relativ einfach: Werfen Sie ein Datenelement aus und empfangen Sie es dann mit Catch. Der Datentyp von throw kann beliebig sein, es kann also natürlich auch ein Objekt sein:

struct Test
{
    
    
	Test(const char* s, int i, double d): s(s), i(i), d(d) {
    
    };
	const char* s;
	int i;
	double d;
	void print() const
	{
    
    
		printf("%s %d %.2f\n", s, i, d);
	}
};
int main()
{
    
    
	try
	{
    
    
		throw Test("LLF", 520, 13.14);
	}
	catch (const Test& e)
	{
    
    
		e.print();
	}
}

2. Grundlegende Leitprinzipien

Eine robuste Fehlerbehandlung ist in jeder Programmiersprache eine Herausforderung. Obwohl Ausnahmen mehrere Funktionen zur Unterstützung einer guten Fehlerbehandlung bieten, können sie nicht alles tun. Um die Vorteile von Ausnahmemechanismen zu nutzen, sollten Sie beim Entwerfen Ihres Codes Ausnahmen berücksichtigen.

  • Verwenden Sie Behauptungen, um nach Fehlern zu suchen, die niemals auftreten sollten. Verwenden Sie Ausnahmen, um auf mögliche Fehler zu prüfen, beispielsweise Fehler bei der Eingabevalidierung öffentlicher Funktionsparameter. Weitere Informationen finden Sie im Abschnitt Ausnahmen und Behauptungen .
  • Ausnahmen werden verwendet, wenn der Code, der den Fehler behandelt, durch einen oder mehrere dazwischenliegende Funktionsaufrufe vom Code getrennt wird, der den Fehler erkennt. Wenn der Code, der den Fehler behandelt, eng mit dem Code verknüpft ist, der den Fehler erkannt hat, überlegen Sie, ob Sie stattdessen den Fehlercode in einer leistungskritischen Schleife verwenden möchten.
  • Geben Sie für jede Funktion, die Ausnahmen auslösen oder weitergeben kann, eine von drei Ausnahmegarantien an: stark, einfach oder nicht auslösen (noexclusive). Weitere Informationen finden Sie unter So entwerfen Sie die Ausnahmesicherheit .
  • Ausnahmen nach Wert auslösen, Ausnahmen nach Referenz abfangen. Fangen Sie nichts, mit dem Sie nicht umgehen können.
  • Verwenden Sie keine Ausnahmespezifikationen, die in C++11 veraltet sind. Weitere Informationen finden Sie in den Abschnitten „Ausnahmespezifikationen“ und „noexclusive“ .
  • Verwenden Sie beim Anwenden Standardbibliotheksausnahmetypen. Leiten Sie benutzerdefinierte Ausnahmetypen aus der Ausnahmeklassenhierarchie ab.
  • Escapes von Destruktoren oder Funktionen zur Speicherfreigabe sind nicht zulässig.

3. Ausnahmeklasse

Aus der Analyse des obigen Codes können wir erkennen, dass das Auslösen eines Objekts anstelle eines einfachen Datentyps beim Auftreten einer Ausnahme mehr Fehlerinformationen übermitteln kann. In diesem Fall müssen jedoch unterschiedliche Klassen für unterschiedliche Ausnahmesituationen definiert werden. Gibt es eine einheitliche Lösung?

C++ bietet eine Standard-Ausnahmeklasse Exception. Schauen Sie sich die Definition an:

/**
* @brief Base class for all library exceptions.
*
* This is the base class for all exceptions thrown by the standard
* library, and by certain language expressions. You are free to derive
* your own %exception classes, or use a different hierarchy, or to
* throw non-class data (e.g., fundamental types).
*/
class exception
{
    
    
public:
	exception() noexcept {
    
     }
	virtual ~exception() noexcept;
	exception(const exception&) = default;
	exception& operator=(const exception&) = default;
	exception(exception&&) = default;
	exception& operator=(exception&&) = default;
	/** Returns a C-style character string describing the general cause
	* of the current error. */
	virtual const char* what() const noexcept;
};

Die Hauptsache besteht darin, eine virtuelle Funktion von what zu definieren, die einen C_style-String zurückgibt. Ihre Hauptfunktion besteht darin, die Ursache eines Vorfalls zu beschreiben. Bei der Verwendung müssen Sie häufig eine Ausnahmeklasse anpassen:

#include<exception>
#include<iostream>
using namespace std;
class MyException:public exception
{
    
    
public:
	const char* what()const throw(){
    
     //throw () 表示不允许任何异常产生
		return "ERROR! Don't divide a number by integer zero.\n";
	}
};
void check(int y) throw(MyException)
{
    
     //throw (MyException)表示只允许myException的异常发生
	if(y==0) throw MyException();
}
int main()
{
    
    
	int x=100,y=0;
	try{
    
    
		check(y);
		cout<<x/y;
	}catch(MyException& me){
    
    
		cout<<me.what();
		cout << "finish exception\n";
		return -1;
	}
	cout << "finish ok\n";
	return 0;
}


4. Standardausnahmeerweiterung

C++ definiert einige Standardausnahmen für verschiedene Szenarien. Sie erben alle von std::Exception:
Fügen Sie hier eine Bildbeschreibung ein
Die folgende Tabelle enthält eine Beschreibung jeder Ausnahme, die in der obigen Hierarchie erscheint:

abnormal beschreiben
std::Exception Diese Ausnahme ist die übergeordnete Klasse aller Standard-C++-Ausnahmen.
std::bad_alloc Diese Ausnahme kann über new ausgelöst werden.
std::bad_cast Diese Ausnahme kann über Dynamic_cast ausgelöst werden.
std::bad_Exception Dies ist nützlich, wenn unerwartete Ausnahmen in C++-Programmen behandelt werden.
std::bad_typeid Diese Ausnahme kann über typeid ausgelöst werden.
std::logic_error Anomalien, die theoretisch durch das Lesen des Codes erkannt werden können.
std::domain_error Diese Ausnahme wird ausgelöst, wenn ein ungültiges mathematisches Feld verwendet wird.
std::invalid_argument Diese Ausnahme wird ausgelöst, wenn ungültige Parameter verwendet werden.
std::length_error Diese Ausnahme wird ausgelöst, wenn ein zu langer std::string erstellt wird.
std::out_of_range Diese Ausnahme kann von Methoden wie std::vector und std::bitset<>::operator ausgelöst werden.
std::runtime_error Anomalien, die theoretisch durch das Lesen des Codes nicht erkannt werden können.
std::overflow_error Diese Ausnahme wird ausgelöst, wenn ein mathematischer Überlauf auftritt.
std::range_error Diese Ausnahme wird ausgelöst, wenn versucht wird, einen Wert außerhalb des Bereichs zu speichern.
std::underflow_error Diese Ausnahme wird ausgelöst, wenn ein mathematischer Unterlauf auftritt.

Beispiel: std::Exception_ptr

Laut der offiziellen Dokumentation ist std::Exception_ptr ein gemeinsam genutzter intelligenter Zeiger, der auf das Ausnahmeobjekt zeigt.

Der Schlüssel liegt darin, zu verstehen, was ein „Ausnahmeobjekt“ ist. Ist es ein Objekt der Klasse std::Exception? Dieses Verständnis ist ungenau. Nach meinem Verständnis sollte das sogenannte „Ausnahmeobjekt“ das von throw geworfene Objekt sein. Nach unserem obigen Lernen kann es sich entweder um einen einfachen Datentyp wie int oder double oder um einen selbstdefinierten Datentyp handeln. definiertes Objekt. Das definierte Klassenobjekt kann natürlich auch ein std::Exception-Klassenobjekt sein.

Es gibt vier Funktionen, die auf std::Exception_ptr angewendet werden:

Die ersten beiden werden verwendet, um einen std::Exception_ptr zu generieren, und der letzte wird verwendet, um das Ausnahmeobjekt, auf das „Exception_ptr“ zeigt, erneut auszulösen (das Wort „rethrow“ ist relativ zu „current_Exception“).

Schauen Sie sich einfach den offiziellen Code an.

Ausnahme_ptr-Beispiel:

#include <iostream> // std::cout
#include <exception> // std::exception_ptr, std::current_exception,
std::rethrow_exception
#include <stdexcept> // std::logic_error
int main ()
{
    
    
	std::exception_ptr p;
	try {
    
    
		throw std::logic_error("some logic_error exception"); // throws
	} catch(const std::exception& e) {
    
    
		p = std::current_exception();
		std::cout << "exception caught, but continuing...\n";
	}
	std::cout << "(after exception)\n";
	try {
    
    
		std::rethrow_exception (p);
	} catch (const std::exception& e) {
    
    
		std::cout << "exception caught: " << e.what() << '\n';
	}
	return 0;
}
  • Zunächst wird eine std::Exception_ptr-Variable p definiert.
  • Dann wird beim ersten Versuch eine Standardausnahme (siehe oben) ausgelöst.
  • Im ersten Catch wird current_Exception() aufgerufen, sodass p auf das erfasste Ausnahmeobjekt zeigt.
  • Rufen Sie dann im zweiten Versuch rethrow_Exception auf, um die Ausnahme erneut auszulösen.
  • Beim zweiten Fang wurde das Ausnahmeobjekt dann immer noch normal erfasst.

make_Exception_ptr Beispiel:

#include <iostream> // std::cout
#include <exception> // std::make_exception_ptr, std::rethrow_exception
#include <stdexcept> // std::logic_error
int main ()
{
    
    
	auto p = std::make_exception_ptr(std::logic_error("logic_error"));
	try {
    
    
		std::rethrow_exception (p); // 重新抛出异常
	} catch (const std::exception& e) {
    
    
		std::cout << "exception caught: " << e.what() << '\n'; // 捕获异常
	}
	return 0;
}

  • Zunächst wird eine Ausnahme make_Exception_ptr erstellt.
  • Dann werfen Sie die Ausnahme in try.
  • Fangen Sie dann die ausgelöste Ausnahme in Catch ab.

Verschachtelte Ausnahmen: nested_Exception::nested_ptr .

#include <iostream> // std::cerr
#include <exception> // std::exception, std::throw_with_nested,
std::rethrow_if_nested
#include <stdexcept> // std::logic_error
// recursively print exception whats:
void print_what (const std::exception& e)
{
    
    
	std::cout << __FUNCTION__ << ", L"<< __LINE__ << ", what:" << e.what() <<'\n';
	try {
    
    
		std::rethrow_if_nested(e);
	} catch (const std::exception& nested) {
    
    
		std::cerr << "nested: ";
	print_what(nested);
	}
}
// throws an exception nested in another:
void throw_nested()
{
    
    
	try {
    
    
		throw std::logic_error ("first");
	} catch (const std::exception& e) {
    
    
		std::throw_with_nested(std::logic_error("second"));
	}
}
int main ()
{
    
    
	try {
    
    
		std::cout << __FUNCTION__ << ", L"<< __LINE__ << std::endl;
		throw_nested();
	} catch (std::exception& e) {
    
    
		std::cout << __FUNCTION__ << ", L"<< __LINE__ << std::endl;
		print_what(e);
	}
	return 0;
}

Zusammenfassen

Durch das Studium dieses Artikels erhalten Sie ein tieferes Verständnis der C++-Ausnahmebehandlung. Erfahren Sie, wie Sie mit der grundlegenden Syntax Ausnahmen behandeln und grundlegende Richtlinien für die Ausnahmebehandlung befolgen. Erfahren Sie außerdem, wie Sie benutzerdefinierte Ausnahmeklassen erstellen und Standardausnahmeklassen erweitern. Erfahren Sie, wie Sie mit std::Exception_ptr Ausnahmezeiger und Ausnahmebehandlungsmethoden in einer Multithread-Umgebung verarbeiten.

Studienempfehlungen:

Fügen Sie hier eine Bildbeschreibung ein

おすすめ

転載: blog.csdn.net/Long_xu/article/details/135349552