Löschen von virtuellen Funktion in einer abgeleiteten Klasse
Habe ich eine virtuelle Basisklasse Funktion, die sollten nie benutzt werden, in einer bestimmten abgeleiteten Klasse. Gibt es eine Möglichkeit zu "löschen"? Ich kann es natürlich nur geben Sie eine leere definition, aber ich würde lieber machen versuchten verwenden, werfen Sie einen compile-Zeit-Fehler. Die C++11 delete
Planer scheint, wie das, was ich möchte, aber
class B
{
virtual void f();
};
class D : public B
{
virtual void f() = delete; //Error
};
nicht kompilieren; gcc, zumindest explizit nicht lassen Sie mich löschen Sie eine Funktion, die eine nicht-gelöscht-base-version. Gibt es einen anderen Weg, um die gleiche Funktionalität zu erhalten?
Wie ist es möglich? Möchten Sie die compiler-Fehlermeldung in
Wenn der Zauber auf Zeiger auf Basisklasse, dann sollte es wirken wie ein Zeiger auf Basisklasse,--in dem Fall
Und welches Verhalten würden Sie erwarten, dass in diesem Fall dann?
Geändert, meine Antwort zu Bryan Chen Frage, vergaß für einen moment, dass die abgeleitete Klasse Funktionen noch genannt, bekommen nach dem casting einen Zeiger auf Basisklasse.
Ich glaube, Sie missverstanden die Nutzung der
D *d = new D(); static_cast<B *>(d)->f()
?Wenn der Zauber auf Zeiger auf Basisklasse, dann sollte es wirken wie ein Zeiger auf Basisklasse,--in dem Fall
D::f
würde noch genannt, bekommen, also ja.Und welches Verhalten würden Sie erwarten, dass in diesem Fall dann?
Geändert, meine Antwort zu Bryan Chen Frage, vergaß für einen moment, dass die abgeleitete Klasse Funktionen noch genannt, bekommen nach dem casting einen Zeiger auf Basisklasse.
Ich glaube, Sie missverstanden die Nutzung der
delete
Schlüsselwort. Die delete
Schlüsselwort ist nicht vorgesehen, die remove-Methode aus der Kind-Klasse, die würden brechen Regeln der Vererbung, die es verwendet werden soll, deaktivieren Sie bestimmten Anruf (wie kopieren aufrufen oder aufrufen von bestimmten Typ von Parametern).InformationsquelleAutor Matt Phillips | 2014-07-07
Du musst angemeldet sein, um einen Kommentar abzugeben.
Es ist nicht erlaubt, von der Norm, aber Sie könnte verwenden Sie eine der folgenden zwei Problemumgehungen, um ein ähnliches Verhalten.
Die erste wäre, die Nutzung
using
zu ändern Sie die Sichtbarkeit der Methode auf private, damit andere daran hindern, es zu benutzen. Das problem bei dieser Lösung ist, dass der Aufruf der Methode auf einen Zeiger der super-Klasse nicht zu einem Kompilierungsfehler.Die beste Lösung, die ich bis jetzt gefunden habe um einen compile-Zeit-Fehler beim Aufruf von
D
s Methode ist die Verwendung einesstatic_assert
mit einer generischen Struktur, die erbt vonfalse_type
. So lange, wie niemand jemals ruft die Methode, die Struktur bleibt undefied und diestatic_assert
nicht scheitern., Wenn die Methode aufgerufen wird, jedoch, die Struktur ist definiert und der Wert false ist, also die
static_assert
ausfällt.Wenn die Methode nicht aufgerufen wird,, aber Sie versuchen, rufen Sie es auf einen Zeiger der super-Klasse, dann
D
s Methode ist nicht definiert und du bekommst einenundefined reference
Kompilierungsfehler.Andere Abhilfe wäre zu eine exception werfen, wenn die Methode verwendet wird, aber das würde nur werfen auf Laufzeit.
B* d = new D(); d->f();
wird ohne Fehler ausgeführt (undB::f
wird aufgerufen), aber dies ist die Art von situation, die ich möchte, würde ein Fehler auftreten.als Sie erklärte in Ihren Kommentaren, dass Verhalten hätte sowieso erwartet, oder bin ich da falsch?
Nein, ich würde nie wollen, dass die Basisklassenversion aufgerufen. Meine Antwort auf Bryan Chen wurde aktualisiert, nachdem Sie antwortete, ich würde wollen, dass ein compiler-Fehler in diesem Fall.
Ich aktualisiert meine Antwort.
wenn Sie in
static_assert(false);
würde er nie kompilieren. Die Vorlage struct wird nur initialisiert, wenn die Methode verwendet wird.InformationsquelleAutor Theolodis
Den standard können Sie nicht löschen alle member einer Basisklasse in einer abgeleiteten Klasse mit gutem Grund:
Tun, so bricht der Vererbung, speziell der "ist-ein" - Beziehung.
Für Verwandte Gründe, es nicht zulassen, dass eine abgeleitete Klasse eine Funktion definieren, die gelöscht werden in der Basis-Klasse:
Der Haken ist nicht mehr Teil der Basis-Klasse Vertragsabschluss und damit es verhindert, dass Sie sich auf Vorherige Garantien nicht mehr halten.
Wenn Sie wollen, schwierig werden, Sie kann Kraft ein Fehler sein, aber es werden link-Zeit statt compile-time:
Erklären Sie die member-Funktion aber nicht immer definieren (Dies ist nicht 100% garantiert, um zu arbeiten für virtuelle Funktionen wenn).
Besser werfen Sie auch einen Blick in die GCC-deprecated-Attribut für frühere Warnungen
__attribute__ ((deprecated))
.Für details und ähnliche MS magic: C++ markieren als veraltet
delete
speziell, ich will nur die Funktionalität.Tatsächlich, zu tun, produziert eine 'undefined vtable' Fehler, auch wenn
f
nie aufgerufen wird, wie gezeigt, hier.Einige Compiler/Linker nicht beschweren, wenn die Implementierung der virtuellen Methode gibt es nicht. Manchmal, je nach compiler-Optionen. Immer noch die beste Wette ist immer Missbilligung.
InformationsquelleAutor Deduplicator
In gewisser Hinsicht ist das ein Widerspruch. Der ganze Sinn von virtuellen Funktionen ist, um verschiedene Implementierungen der Vertrag von der Basisklasse bereitgestellt wird. Was Sie versuchen zu tun brechen die Vertrag. Der C++ - Sprache ist entworfen, um zu verhindern, dass Sie tun. Dies ist der Grund, warum es zwingt Sie zu implementieren, rein virtuelle Funktionen, wenn Sie instanziieren ein Objekt. Und das ist, warum es nicht zulassen, Sie zu löschen Teil des Vertrag.
Was geschieht, ist eine gute Sache. Es ist wahrscheinlich verhindern, dass Sie von der Umsetzung eines ungeeigneten design-Wahl.
Jedoch:
Manchmal kann es sinnvoll sein, eine leere Implementierung, die nichts tut:
Oder eine leere Implementierung, liefert einen "failed" status:
Alles hängt davon ab, was Sie zu tun versuchen. Vielleicht, wenn Sie ihm geben könnte, mehr Informationen, was du zu erreichen versuchst, jemand kann Sie in die richtige Richtung.
BEARBEITEN
Wenn Sie darüber nachdenken, zu vermeiden, den Aufruf der Funktion für ein bestimmtes abgeleiteten Typ, der Anrufer muss wissen, welche Art es ist Berufung. Der springende Punkt, der den Aufruf einer Basis-Klasse Referenz/Zeiger ist, dass Sie nicht wissen, welcher abgeleitete Typ wird empfangen Sie den Anruf.
static_cast
unddynamic_cast
. Manchmal ist die abgeleitete Klasse Identität Angelegenheiten, die für den aufrufenden code, und dies eine jener Zeiten.Es ist definitiv manchmal wichtig, um sich direkt mit der abgeleiteten Klasse. Dies ist in der Tat was dynamic_cast verwendet wird. Aber dann sind Sie nicht mehr dem Aufruf der Basisklasse Verweis/Zeiger, so dass meine Aussage nicht wirklich gelten. Aber ich hoffte, es würde Ihre Frage zu beantworten, warum es nicht erlaubt.
InformationsquelleAutor Galik
Was Sie tun können, ist einfach das werfen einer exception in der abgeleiteten Implementierung. Zum Beispiel, das Java Collections framework macht das sehr, übermäßig: Wenn Sie eine update-operation wird auf eine collection, die unveränderlich ist, wird die entsprechende Methode wirft eine
UnsupportedOperationException
. Sie können das gleiche in C++.Natürlich, dies zeigt eine schädliche Verwendung der Funktion erst zur Laufzeit; nicht zur compile-Zeit. Aber mit virtuellen Methoden, Sie sind nicht in der Lage zu fangen, die solche Fehler zur compile-Zeit, da eh Polymorphismus. E. g.:
Hier, speichern Sie eine
D
imB*
variable. Also, auch wenn es war ein Weg, zu sagen, der compiler, dass Sie nicht nennen dürfenf
auf eineD
der compiler nicht imstande sein würden, um diesen Fehler zu melden hier, denn es sieht nurB
.InformationsquelleAutor gexicide
C++11 bietet eine keyword -
final
die verhindert, dass eine virtuelle Funktion überschrieben.Aussehen: http://en.cppreference.com/w/cpp/language/final .
InformationsquelleAutor sergs