2010-11-25 8 views
7

Ich entwickle eine Klasse, die als Container für eine andere Klasse dient. In der Containerklasse muss ich eine Methode implementieren, um alle Elemente in der Auflistung abzurufen. Meine Container-Klasse verwendet ein Std :: Deque.C++ Entwerfen von Containern und Verwalten der Listenrückgabe

Sollte ich einen Verweis auf die Deque zurückgeben? Sollte ich eine Kopie der Deque zurückgeben? (mein Gott sag mir, das ist nicht die Antwort ... :)) Sollte ich ein Array zurückgeben? ... Was ist die beste Praxis in diesem Zusammenhang? Danke

+2

Fragen Sie sich, warum Sie Zugriff auf die internen Objekte außerhalb Ihrer Containerklasse benötigen. Sie könnten die Kapselung brechen. Vielleicht können Sie eine Methode hinzufügen, mit der Sie jedes interne Objekt ändern können, indem Sie eine Funktion übergeben? –

Antwort

6

Die beste Praxis IMHO ist die iterator design pattern und zurück Iteratoren

Soweit Ihr spezielles Beispiel zu verwenden ist besorgt ich so etwas tun würde:

class myContainer 
{ 
public: 
    typedef std::deque<X> actual_container_type; 
    typedef actual_container_type::iterator iterator; 
    typedef actual_container_type::const_iterator const_iterator; 
    //etc... 

    iterator begin() {return cont.begin(); } 
    const_iterator begin() const {return cont.begin(); } 
    iterator end() {return cont.end(); } 
    const_iterator end() const {return cont.end(); } 

    //you may choose to also provide push_front and push_back... or whatever :) 


    private: 
    actual_container_type cont; 
} 
+2

In Ihrem Fall stammen die von Ihnen zurückgegebenen Iteratoren aus vorhandenen STL-Klassen. Ein Benutzer, der seine eigenen Iteratoren zurückgibt, sollte seine Klasse von std :: iterator ableiten, damit er die Eigenschaften und typedefs übernimmt. Dies ist nicht notwendig, wenn der Iterator ein Zeiger ist. – CashCow

+0

Danke, es macht Sinn. Jetzt kann ich besser bewegen und klar sehen ... Danke :) – Andry

+0

Ah, sorry, eine Sache ... Wie weiß der Compiler, wenn ich den Iterator begin() oder const_iterator begin()? – Andry

0

Einfach wickeln begin(), end() , size() und operator[] und alles wird gut.

+0

Ja, danke, das ist ein gängiges Muster in C++, :) Es ist auffällig, dass ich kein Experte bin ... :) Aber nur eine Frage: Wie weiß der Compiler, wenn ich den Iterator begin() oder beginne const_iterator beginnen()? – Andry

+0

Das hängt vom Kontext ab, wenn Ihr Objekt const ist, wird es const_iterator begin() const; Beachten Sie, dass es die rechte Konstante ist, die überlastet wird, nicht der Rückgabetyp. –

0

Das Iterator-Muster ist nett, aber Sie legen Ihre Implementierungsdetails offen. Es kann missbraucht werden und Ihre Klasseninterna brechen. Dies sind alles schlechte Dinge für eine API, und Sie sollten Schutz dagegen entwerfen.

Es klingt verschwenderisch, aber der sicherste Weg ist, eine Kopie Ihrer Sammlung als std :: vector zurückzugeben.

+0

Aber ein const_iterator würde sowieso eine Ausgabe zu einem const_iterator auf dem kopierten std :: vector liefern, oder? (Es sei denn, die Daten wurden in der Zwischenzeit geändert, aber dann sind alle Wetten aus ...) Mein Punkt ist, dass den Interna kein Schaden zugefügt werden konnte. Stellen Sie einfach keinen nichtkonstanten Iterator zur Verfügung. – btown

+0

const_iterator ist in Ordnung. Sie können jedoch Probleme bekommen, wenn Sie die Implementierung Ihrer Sammlung ändern, dh wenn Sie die Sammlung in eine Karte ändern, erwarten Sie nicht, dass Ihre Iteratoren so funktionieren wie sie. Und wenn Sie darauf hinweisen, dass das Ändern der Daten von einem anderen Thread die Iteratoren ungültig macht, von denen Sie nicht unbedingt wissen werden. Kopieren ist am sichersten! – DanDan