Ich habe mich gefragt, vielleicht wäre es besser, Mehrfachvererbung zu verwenden, die von jedem des abgeleiteten std :: Ausnahmeklassen
Hinweis zu erben, dass dies ein Problem ist, aufgrund der Tatsache, dass Die Ausnahmen in der Standardbibliothek leiten sich nicht virtuell voneinander ab. Wenn Sie die Mehrfachvererbung einführen, erhalten Sie die gefürchtete Diamantenausnahmehierarchie ohne virtuelle Vererbung und können keine abgeleiteten Ausnahmen von std::exception&
abfangen, da Ihre abgeleitete Ausnahmeklasse zwei Unterobjekte von std::exception
trägt, was std::exception
zu einer "mehrdeutigen Basisklasse" macht.
Konkretes Beispiel:
class my_exception : virtual public std::exception {
// ...
};
class my_runtime_error : virtual public my_exception
, virtual public std::runtime_error {
// ...
};
Jetzt my_runtime_error
herleitet (indirekt) von std::exception
zweimal, einmal durch std::run_time_error
und einmal durch my_exception
.Da der ehemalige leitet sich nicht von std::exception
praktisch, diese
try {
throw my_runtime_error(/*...*/);
} catch(const std::exception& x) {
// ...
}
wird nicht funktionieren.
Edit:
Ich glaube, ich habe das erste Beispiel einer Ausnahmeklassenhierarchie denen MI in einem der Stroustrup Bücher gesehen, also habe ich festgestellt, dass im Allgemeinen ist es eine gute Idee ist. Dass die Ausnahmen der std lib nicht virtuell voneinander abstammen, halte ich für einen Misserfolg.
Als ich zuletzt eine Ausnahmehierarchie entworfen habe, habe ich MI sehr ausgiebig verwendet, aber nicht von den Ausnahmeklassen der std lib abgeleitet. In dieser Hierarchie gab es abstrakte Ausnahmeklassen, die Sie so definierten, dass Ihre Benutzer sie abfangen konnten, und entsprechende Implementierungsklassen, die von diesen abstrakten Klassen und von einer Implementierungsklasse stammten, die Sie tatsächlich werfen würden. Um dieses zu erleichtern, definiert ich einige Vorlagen, die all die harte Arbeit tun würde:
// something.h
class some_class {
private:
DEFINE_TAG(my_error1); // these basically define empty structs that are needed to
DEFINE_TAG(my_error2); // distinguish otherwise identical instances of the exception
DEFINE_TAG(my_error3); // templates from each other (see below)
public:
typedef exc_interface<my_error1> exc_my_error1;
typedef exc_interface<my_error2> exc_my_error2;
typedef exc_interface<my_error3,my_error2> // derives from the latter
exc_my_error3;
some_class(int i);
// ...
};
//something.cpp
namespace {
typedef exc_impl<exc_my_error1> exc_impl_my_error1;
typedef exc_impl<exc_my_error2> exc_impl_my_error2;
typedef exc_impl<exc_my_error3> exc_impl_my_error3;
typedef exc_impl<exc_my_error1,exc_my_error2> // implements both
exc_impl_my_error12;
}
some_class::some_class(int i)
{
if(i < 0)
throw exc_impl_my_error3(EXC_INFO // passes '__FILE__', '__LINE__' etc.
, /* ... */ // more info on error
);
}
Mit Blick auf diese jetzt zurück, ich glaube, ich hätte gemacht, dass exc_impl
Klassenvorlage aus std::exception
ableiten (oder jede andere Klasse in der std lib exception hierarchy, übergeben als optionaler Template-Parameter), da sie niemals von einer anderen exc_impl
Instanz abgeleitet wird. Aber damals war das nicht nötig, also kam es mir nie in den Sinn.
Was würden Sie davon gewinnen? Warum nicht einzelne Vererbung verwenden? Warum müssen Sie * sowohl * Exception als auch RuntimeError Exceptions anpassen? – jalf
Ich wollte die Funktionalität meiner benutzerdefinierten Exception-Klasse verwenden und die STL-Ausnahmehierarchie beibehalten, indem ich von den verschiedenen abgeleiteten std :: exception-Klassen erbte. Auf diese Weise ein Haken mit: versuchen { werfen RunTimeException (...); } catch (std :: runtime_error & e) { ... } fangen (std :: Ausnahme & e) {...} würde auch meine benutzerdefinierte Klasse fangen. Es könnte jedoch eine Zeitverschwendung sein. Ich könnte auch nur versuchen { throw RunTimeException (...); } catch (std :: exception & e) {...} aber es könnte es schwieriger machen, die Ausnahme dann zu identifizieren. Ich weiß nicht, was die typische Praxis bei benutzerdefinierten Ausnahmeklassen ist? – Tom
Ich würde sagen, nur erben von Std :: Runtime_error (oder Std :: Ausnahme). Wenn Sie eine bestimmte Behandlung wünschen, können Sie doch mehrere Fänge haben. –