const vector mit nicht const Elementen möglich?



  • gegeben:

    class Foo
    {
    public:
       int ba;
       Foo(int b = 42):ba(b){}
    
    }
    
    void main(){
        std::vector<Foo> f{1,2,3,4,5}
    }
    

    wie mache ich nun eine Funktion, die den vector f nicht verändern kann jedoch von jedem Eelement die variable ba?

    mit:

    fun(const std::vector<Foo> ff){
           ff[0].ba = 42;  //geht nicht
    }
    

    sind auch die Elemente konstant:


  • Mod

    Das ist keine Frage des Vectors, sondern von Foo. Es muss Foo::ba mutable sein:

    #include <vector>
    class Foo
    {
    public:
       mutable int ba;
       Foo(int b = 42):ba(b){}
    
    };
    
    void fun(const std::vector<Foo> ff){
           ff[0].ba = 42;  //geht
    }
    


  • und wenn ich dann noch eine Funktion haben will die auch "ba" nicht ändern darf?



  • Dann nimm vector von pointern. Ich denke, das ist was du willst:

    #include <vector>
    class Foo
    {
    public:
       int ba;
       Foo(int b = 42):ba(b){}
    
    };
    
    void fun(const std::vector<Foo*> ff){
           ff[0]->ba = 42;  //geht
           ff.push_back(nullptr); // geht nicht
    }
    


  • Danke für die Antworten.
    Ich hatte sowas auch gerade gefunden.
    Auf der Seite (leider schon wieder zu gemacht und finde nicht mehr) stand aber auch, dass es gefährlich sein kann und mann das idR lieber nicht machen sollte.



  • Da ist pauschal nichts gefährlich. Zumindest solange du dich an 'non owning raw pointers' hälst.



  • D.h. nur smartpointer verwenden?


  • Mod

    vector0815 schrieb:

    D.h. nur smartpointer verwenden?

    Nein, nicht pauschal. Smartpointer dann, wenn man einen Besitz oder eine Verantwortlichkeit ausdrücken will. Rohe Zeiger dann, wenn man einen einfachen Verweis ausdrücken möchte.

    Hier klingt das eher so, als sollen die Objekte selbst ganz woanders leben (d.h. die Verantwortung liegt ganz woanders), während in dem Vektor wohl nur Verweise auf diese Objekte sein sollen.



  • Nein, d.h. eher, dass innerhalb einer call-hierarchie keiner an einem raw-pointer rumpfuscht. Und ja, irgendwo steckt der Zeiger am besten in einem unique_pointer.
    Ich weiss, Beschreibung ist sehr dürftig, aber das kann man bestimmt irgendwo besser nachlesen, als ich das jetzt beschreiben könnte.



  • vector0815 schrieb:

    D.h. nur smartpointer verwenden?

    Ja, also z.B. einen std::vector<std::unique_ptr<T>> verwenden. Wenn die Objekte im Vector (also hier sizeof(T) allerdings klein sind, ist es wirklich die Frage, ob man diese Indirektion unbedingt haben will. Nur um das const (also was "Java-final"-ähnliches) zu bekommen - das finde ich als Argument zu schwach.

    (Edit: also natürlich unter der Voraussetzung, dass der Vector auch Owner ist, ansonsten natürlich keine Smart-Ptr)

    Wäre es nicht besser, in ff 2 Iteratoren als Parameter zu nehmen? Das löst zwar nicht direkt dein Problem, aber wenn du fun(begin(v), end(v)) aufrufst, ist auch von außen ersichtlich, dass sich "das äußere v" nicht ändert, während über die v-Elemente nichts bekannt ist.



  • wob schrieb:

    Ja, also z.B. einen std::vector<std::unique_ptr<T>> verwenden. Wenn die Objekte im Vector (also hier sizeof(T) allerdings klein sind, ist es wirklich die Frage, ob man diese Indirektion unbedingt haben will. Nur um das const (also was "Java-final"-ähnliches) zu bekommen - das finde ich als Argument zu schwach.

    jedes Element hat ein ~500er double array und noch paar Variablen.

    wob schrieb:

    (Edit: also natürlich unter der Voraussetzung, dass der Vector auch Owner ist, ansonsten natürlich keine Smart-Ptr)

    Habe bisher noch nix mit smart pointer gemacht. Wann ist vector ein Owner?

    wob schrieb:

    Wäre es nicht besser, in ff 2 Iteratoren als Parameter zu nehmen? Das löst zwar nicht direkt dein Problem, aber wenn du fun(begin(v), end(v)) aufrufst, ist auch von außen ersichtlich, dass sich "das äußere v" nicht ändert, während über die v-Elemente nichts bekannt ist.

    Das war nur als kleines Beispiel. Der vector zeigt dann nur auf ausgewählte Objekte eines anderen viel größeren vectors. Die Auswahl wird dann immer komplett verwendet.

    Oder könnte man den großen Vektor kopieren (ohne die Daten zu kopieren) und ihn dann umsortieren bzw. Elemente löschen (jedoch nicht die Daten)



  • vector0815 schrieb:

    jedes Element hat ein ~500er double array und noch paar Variablen.

    Also ca. 4kB. Das würde ich in diesem Zusammenhang nicht als klein bezeichnen.

    vector0815 schrieb:

    wob schrieb:

    (Edit: also natürlich unter der Voraussetzung, dass der Vector auch Owner ist, ansonsten natürlich keine Smart-Ptr)

    Habe bisher noch nix mit smart pointer gemacht. Wann ist vector ein Owner?

    Das musst du entscheiden! Irgendwer muss die Objekte ja besitzen und am Ende wieder zerstören.

    vector0815 schrieb:

    wob schrieb:

    Übergabe als Iteratoren

    Das war nur als kleines Beispiel. Der vector zeigt dann nur auf ausgewählte Objekte eines anderen viel größeren vectors. Die Auswahl wird dann immer komplett verwendet.

    Ok, also besitzt der vector NICHT die Daten, denn der viel größere ist Owner. Zumindest wenn ich dich richtig verstanden habe. Aber dann ist mir jetzt nicht klar, warum in deinem Beispiel der vector keine Pointer hatte. Der vector zeigt ja NICHT auf einen anderen vector, sondern es ist ja ein separater vector. Aber gerade dann wäre doch ein Iteratorpaar viel sinnvoller, denn das zeigt ja wirklich auf einen Bereich des Vectors. Oder du nimmst sowas wie ein array_view bzw. span.

    vector0815 schrieb:

    Oder könnte man den großen Vektor kopieren (ohne die Daten zu kopieren) und ihn dann umsortieren bzw. Elemente löschen (jedoch nicht die Daten)

    Du könntest eine Kopie mit Pointern auf die Originaldaten erstellen - das waren doch hier auch schon die Vorschläge mit dem nicht-besitzenden Pointer.



  • wob schrieb:

    Ok, also besitzt der vector NICHT die Daten, denn der viel größere ist Owner. Zumindest wenn ich dich richtig verstanden habe. Aber dann ist mir jetzt nicht klar, warum in deinem Beispiel der vector keine Pointer hatte. Der vector zeigt ja NICHT auf einen anderen vector, sondern es ist ja ein separater vector. Aber gerade dann wäre doch ein Iteratorpaar viel sinnvoller, denn das zeigt ja wirklich auf einen Bereich des Vectors. Oder du nimmst sowas wie ein array_view bzw. span.

    Es ist aber nicht unbedingt ein Bereich, sondern zB. Elemente 23, 42, 1337, 4311 usw.

    Ich habe nochmal überlegt. Es wäre sinnvoll eine eigene Kopie des Vektors in der Klasse zu haben. Da es auch sein kann, dass der größere Vektor gelöscht wird bevor die Klasse mit der Auswahl gelöscht wird.
    Problem wäre dann aber dass man die Änderung des Objektes (recht rechenintensiv) 2 mal machen müsste.

    Oder könnte man auch mit smartpointern einen vektor der die Objekte als Elemente hat beim löschen bestimmte Elemente auslassen lassen, die noch wo anders verwendet werden?



  • vector0815 schrieb:

    Oder könnte man auch mit smartpointern einen vektor der die Objekte als Elemente hat beim löschen bestimmte Elemente auslassen lassen, die noch wo anders verwendet werden?

    In dem Fall wäre wohl der shared_ptr anstelle des unique_ptr angebracht, dann wird das Objekt erst gelöscht, wenn der letzte Zeiger auf das Objekt gelöscht wird. (http://en.cppreference.com/w/cpp/memory/shared_ptr )



  • Danke, muss ich mir mla anschauen.


  • Mod

    wob schrieb:

    vector0815 schrieb:

    jedes Element hat ein ~500er double array und noch paar Variablen.

    Also ca. 4kB. Das würde ich in diesem Zusammenhang nicht als klein bezeichnen.

    Ich möchte noch hinzu fügen, dass hier die Elemente dafür verantwortlich sein sollten, dass solch große Speicherbereiche sinnvoll verwaltet werden, anstatt dass der Benutzer der Elemente das machen muss. Als Benutzer sollte es mich bei der Wahl der Verwaltungsweise nicht interessieren brauchen, was die Interna der benutzten Objekte sind, sondern nur, was ich mit ihnen machen möchte.

    Das kommt natürlich auch ein bisschen darauf an, was die Klasse hier überhaupt tun soll. wenn die Klasse ein double[500]-Array ohne nennenswerte Funktion ist, dann ist es natürlich ok, wenn die 500-doubles direkt im Objekt liegen. Wenn das eine Klasse ist, die auch etwas tut, dann hat sie gefälligst auch für sinnvolle Verwaltung ihrer internen Daten zu sorgen.


Log in to reply