Ich lerne für eine Prüfung und habe eine Frage, die ich nur schwer geben und beantworten kann.
Warum gibt es keine Iterator-Basisklasse, von der alle anderen Iteratoren erben?
Ich vermute, mein Lehrer bezieht sich auf die hierarchische Struktur aus der CPP-Referenz " http://prntscr.com/mgj542 ", und wir müssen einen anderen Grund angeben, als warum sollten sie?
Ich weiß, was Iteratoren sind (irgendwie) und dass sie verwendet werden, um an Containern zu arbeiten. Soweit ich weiß, haben unterschiedliche Container aufgrund der unterschiedlichen möglichen zugrunde liegenden Datenstrukturen unterschiedliche Iteratoren, da Sie beispielsweise zufällig auf ein Array zugreifen können, jedoch nicht auf eine verknüpfte Liste, und unterschiedliche Container unterschiedliche Arten des Durchlaufs erfordern.
Es handelt sich wahrscheinlich um spezialisierte Vorlagen, je nach Container, oder?
Antworten:
Sie haben bereits Antworten erhalten, die darauf hinweisen, warum nicht alle Iteratoren von einer einzelnen Iterator-Basisklasse erben müssen. Ich war allerdings noch ein Stück weiter gekommen. Eines der Ziele von C ++ ist die Abstraktion ohne Laufzeitkosten.
Wenn Iteratoren von allen arbeiten, die von einer gemeinsamen Basisklasse erben, und virtuelle Funktionen in der Basisklasse verwenden, um die Schnittstelle zu definieren, und die abgeleiteten Klassen Implementierungen dieser virtuellen Funktionen bereitstellen, könnte (und würde dies häufig) eine erhebliche Laufzeit hinzufügen Overhead für die beteiligten Operationen.
Betrachten wir zum Beispiel eine einfache Iteratorhierarchie, die Vererbung und virtuelle Funktionen verwendet:
Dann lassen Sie es uns kurz testen:
[Hinweis: Abhängig von Ihrem Compiler müssen Sie möglicherweise etwas mehr tun, z. B. die Definition der Iteratorkategorie, des Differenztyps, der Referenz usw., damit der Compiler den Iterator akzeptiert.]
Und die Ausgabe ist:
[Wenn Sie den Code ausführen, stimmen Ihre Ergebnisse natürlich nicht genau mit diesen überein.]
Selbst für diesen einfachen Fall (und nur etwa 80 Inkremente und Vergleiche) haben wir einer einfachen linearen Suche einen Overhead von etwa 60% hinzugefügt. Insbesondere wenn Iteratoren ursprünglich zu C ++ hinzugefügt wurden, hätten einige Leute ein Design mit so viel Aufwand einfach nicht akzeptiert. Sie wären wahrscheinlich nicht standardisiert worden, und selbst wenn sie es getan hätten, würde praktisch niemand sie verwenden.
quelle
Der Unterschied besteht darin, was etwas ist und wie sich etwas verhält.
Viele Sprachen versuchen, die beiden miteinander zu verbinden, aber es sind ganz unterschiedliche Dinge.
Wenn wie ist was und was ist wie ...
Wenn alles davon erbt, ergeben sich
object
einige Vorteile wie: Jede Objektvariable kann jeden Wert enthalten. Aber das ist auch der Knackpunkt , alles muss sich verhalten ( die wie ) wie einobject
und sieht aus wie ( das , was ) einobject
.Aber:
Entweder wird der
object
Typ im Wesentlichen unbrauchbar - da das Objekt keine Gemeinsamkeit für alle möglichen Instanzen bietet. Oder es wird Objekte geben, die eine gebrochene /object
schuhhörnige / absurde Definition einer vermuteten universellen Eigenschaft haben, die bis auf eine Reihe von Fallstricken ein fast universelles Verhalten beweist.Wenn Was nicht mit Wie verbunden ist
Alternativ können Sie das Was und das Wie getrennt halten. Dann können sich mehrere verschiedene Typen (mit überhaupt keinem gemeinsamen Was ) auf die gleiche Weise verhalten, wie es der Mitarbeiter beim Wie gesehen hat . In diesem Sinne ist die Idee eines
Iterator
nicht ein spezifisches Was , sondern ein Wie . Insbesondere wie interagieren Sie mit einer Sache, wenn Sie noch nicht wissen, mit was Sie interagieren ?Java (und ähnliche) ermöglichen Ansätze dazu mithilfe von Schnittstellen. Eine Schnittstelle in dieser Hinsicht beschreibt die Kommunikationsmittel und implizit ein Protokoll der Kommunikation und des Handelns, das befolgt wird. Jedes Was, das sich als von einem bestimmten Wie deklariert , besagt, dass es die relevante Kommunikation und Aktion unterstützt, die im Protokoll beschrieben sind. Dadurch kann jeder Mitarbeiter auf dem verlassen Wie und ab steckenzubleiben nicht exakt angeben , welche Was ‚s verwendet werden kann.
C ++ (und ähnliche) ermöglichen Ansätze hierfür durch Ententypisierung. Einer Vorlage ist es egal, ob der kollaborierende Typ deklariert, dass er einem Verhalten folgt, nur dass innerhalb eines bestimmten Kompilierungskontexts das Objekt auf eine bestimmte Weise interagiert werden kann. Auf diese Weise können C ++ - Zeiger und Objekte, die bestimmte Operatoren überschreiben, von demselben Code verwendet werden. Weil sie die Checkliste erfüllen, um als gleichwertig zu gelten.
Der zugrunde liegende Typ muss nicht einmal einen Container iterieren, es kann auch was sein . Darüber hinaus können einige Mitarbeiter noch allgemeiner arbeiten. Stellen Sie sich vor, eine Funktion wird nur benötigt
a++
. Ein Iterator kann dies erfüllen, ebenso ein Zeiger, eine Ganzzahl und jedes implementierte Objektoperator++
.Unter- und Überspezifikation
Das Problem bei beiden Ansätzen liegt unter und über der Spezifikation.
Für die Verwendung einer Schnittstelle muss das Objekt deklarieren, dass es ein bestimmtes Verhalten unterstützt. Dies bedeutet auch, dass der Ersteller dies von Anfang an ausführen muss. Dies führt dazu, dass einige What 's den Schnitt nicht machen, da sie ihn nicht deklariert haben. Es bedeutet auch, dass immer Was einen gemeinsamen Vorfahren hat, die Schnittstelle, die das Wie darstellt . Dies kehrt zum ursprünglichen Problem von zurück
object
. Dies führt dazu, dass Mitarbeiter ihre Anforderungen zu stark spezifizieren, während gleichzeitig einige Objekte entweder aufgrund fehlender Deklaration unbrauchbar werden oder ausgeblendete Fallstricke auftreten, da ein erwartetes Verhalten schlecht definiert ist.Die Verwendung einer Vorlage erfordert, dass der Mitarbeiter mit einem völlig unbekannten Was arbeitet und durch seine Interaktionen ein Wie definiert . In gewissem Maße erschwert dies das Schreiben von Mitarbeitern, da es das Was für seine Kommunikationsprimitive (Funktionen / Felder / usw.) analysieren und gleichzeitig Kompilierungsfehler vermeiden muss, oder zumindest darauf hinweisen muss, wie ein bestimmtes Was nicht seinen Anforderungen für das Wie entspricht . Dies ermöglicht es dem Mitarbeiter, das absolute Minimum von einem bestimmten Was zu fordern , wodurch der breiteste Bereich dessen verwendet werden kann , was verwendet werden soll. Leider hat dies den Nachteil, dass unsinnige Verwendungen von Objekten zugelassen werden, die technisch die Kommunikationsprimitive für eine bestimmte Person liefernWie , aber befolgen Sie nicht das implizite Protokoll, das das Auftreten aller möglichen schlechten Dinge zulässt.
Iteratoren
In diesem Fall ist ein
Iterator
ist ein Wie es eine Abkürzung für eine Beschreibung der Wechselwirkung ist. Alles, was dieser Beschreibung entspricht, ist per Definition einIterator
. Wenn wir wissen, wie wir allgemeine Algorithmen schreiben können, haben wir eine kurze Liste mit ' Wie wird ein bestimmtes Was gegeben ? ', Das bereitgestellt werden muss, damit der Algorithmus funktioniert. Diese Liste enthält die Funktion / Eigenschaften / usw. Ihre Implementierung berücksichtigt die spezifischen Aspekte , mit denen sich der Algorithmus befasst.quelle
Da C ++ nicht brauchen zu haben (Auszug) Basisklassen Polymorphismus zu tun. Es hat sowohl strukturelle Subtypisierung als auch nominative Subtypisierung .
Verwirrenderweise im speziellen Fall von Iteratoren wurden frühere Standards
std::iterator
als (ungefähr) definiert.Dh lediglich als Anbieter der erforderlichen Mitgliedstypen. Es hatte kein Laufzeitverhalten und war in C ++ 17 veraltet
Beachten Sie, dass auch dies keine gemeinsame Basis sein kann, da eine Klassenvorlage keine Klasse ist und jede Instanziierung für sich allein steht.
quelle
Ein Grund ist, dass Iteratoren keine Instanzen einer Klasse sein müssen. Zeiger sind zum Beispiel in vielen Fällen vollkommen gute Iteratoren, und da dies Grundelemente sind, können sie nichts erben.
quelle