Ich habe eine Klasse, die eine Liste von Personen darstellt.
class AddressBook
{
public:
AddressBook();
private:
std::vector<People> people;
}
Ich möchte Kunden erlauben, über den Vektor von Menschen zu iterieren. Der erste Gedanke, den ich hatte, war einfach:
std::vector<People> & getPeople { return people; }
Allerdings möchte ich nicht die Details der Implementierung an den Client lecken . Ich möchte möglicherweise bestimmte Invarianten beibehalten, wenn der Vektor geändert wird, und ich verliere die Kontrolle über diese Invarianten, wenn ich die Implementierung verliere.
Was ist der beste Weg, um die Iteration zuzulassen, ohne die Interna zu verlieren?
begin()
undend()
ist gefährlich, da (1) diese Typen Vektoriteratoren (Klassen) sind, die verhindern, dass einer zu einem anderen Container wie a wechseltset
. (2) Wenn der Vektor modifiziert wird (z. B. gewachsen oder einige Elemente gelöscht), könnten einige oder alle Vektoriteratoren ungültig gemacht worden sein.Antworten:
Iteration zuzulassen, ohne die Interna zu verlieren, ist genau das, was das Iterationsmuster verspricht. Das ist natürlich hauptsächlich Theorie. Hier ist ein praktisches Beispiel:
Sie stellen Standards
begin
undend
Methoden wie Sequenzen in der AWL zur Verfügung und implementieren sie einfach durch Weiterleitung an die Methode von vector. Hierdurch werden einige Implementierungsdetails preisgegeben, nämlich, dass Sie einen Vektoriterator zurückgeben, aber kein vernünftiger Client sollte sich jemals darauf verlassen, sodass dies kein Problem darstellt. Ich habe hier alle Überladungen angezeigt, aber natürlich können Sie zunächst nur die const-Version bereitstellen, wenn Clients keine People-Einträge ändern können sollen. Die Verwendung der Standardbenennung hat Vorteile: Jeder, der den Code liest, weiß sofort, dass er eine "Standard" -Iteration bietet und als solche mit allen gängigen Algorithmen, auf Schleifen basierenden Bereichen usw. funktioniert.quelle
begin()
undend()
dieses nur an den Vektor weiterleitenbegin()
undend()
es dem Benutzer ermöglichen, die Elemente im Vektor selbst zu ändern, möglicherweise mithilfe vonstd::sort()
. Abhängig davon, welche Invarianten Sie beibehalten möchten, kann dies akzeptabel sein oder nicht. Es ist jedoch erforderlich, C ++ 11-bereichsbasierte for-Schleifen bereitzustellenbegin()
undend()
zu unterstützen.Wenn Sie nur eine Iteration benötigen, ist möglicherweise ein Wrapper
std::for_each
ausreichend:quelle
const
Iteration. Dasfor_each()
ist eineconst
Mitgliedsfunktion. Daher wird das Mitgliedpeople
als gesehenconst
. Daherbegin()
undend()
wird als überladenconst
. Daher kehren sie zuconst_iterator
s zurückpeople
. Daherf()
wird ein erhaltenPeople const&
. Das Schreibencbegin()
/cend()
hier wird in der Praxis nichts ändern, obwohlconst
ich als obsessiver Benutzer der Ansicht sein könnte, dass es sich immer noch lohnt, dies zu tun, als (a) warum nicht; es sind nur 2 Zeichen, (b) ich sage gerne, was ich meine, zumindest mitconst
, (c) es schützt vor versehentlichem Einfügen an einer anderen Stelleconst
usw.Sie können das Pimpl-Idiom verwenden und Methoden bereitstellen, um den Container zu durchlaufen .
In der Kopfzeile:
In der Quelle:
Auf diese Weise merkt Ihr Client nicht, welche Art von Container Sie verwenden, wenn er das typedef aus dem Header verwendet. Und die Implementierungsdetails sind vollständig verborgen.
quelle
Man könnte Mitgliedsfunktionen bereitstellen:
Die den Zugriff ermöglichen, ohne Implementierungsdetails (wie die Kontiguität) offenzulegen, und diese innerhalb einer Iteratorklasse verwenden:
Iteratoren können dann wie folgt vom Adressbuch zurückgegeben werden:
Wahrscheinlich müssten Sie die Iteratorklasse mit Merkmalen usw. ausstatten, aber ich denke, dass dies tun wird, was Sie gefragt haben.
quelle
Wenn Sie die Funktionen von std :: vector genau implementieren möchten, verwenden Sie die folgende private Vererbung und steuern Sie, was verfügbar gemacht wird.
Bearbeiten: Dies wird nicht empfohlen, wenn Sie auch die interne Datenstruktur, dh std :: vector, ausblenden möchten
quelle
vector
denen in Konflikt stehen , die Sie nie verwenden möchten, aber dennoch erben müssen?) und möglicherweise aktiv gefährlich (was ist, wenn die Klasse, von der Sie träge erben, durch einen Zeiger auf diesen Basistyp irgendwo gelöscht wird, aber [unverantwortlicherweise] nicht vor der Zerstörung von geschützt ist?) Ein abgeleitetes Objekt über einen solchen Zeiger, also einfach zerstören ist UB?)