Kopien und Zuweisungen von Klassenobjekten



  • Lymogry schrieb:

    Call by Reference hats schon getan ... darauf bin ich nicht mehr gekommen, nachdem ich Sternchen gesehen habe! 😃

    pass auf, gleich kommt sone und sagt dir dass das beides call-by-value ist!

    Lymogry schrieb:

    (Warst du das nicht gerade eben?)

    ja weil ich unkreativ bin bzgl. namenswahl.



  • asfasdfas schrieb:

    Lymogry schrieb:

    Call by Reference hats schon getan ... darauf bin ich nicht mehr gekommen, nachdem ich Sternchen gesehen habe! 😃

    pass auf, gleich kommt sone und sagt dir dass das beides call-by-value ist!

    hihihi 😃
    Wir warten! 🙂



  • Ok ... zu früh gefreut????

    void tuwas(std::vector<A*> &boo) {
        A foo3(9,10);
        boo.push_back(&foo3);   // Neues Element als Platzhalter.
        *(boo[boo.size()-1])=foo3;  // Inhalt kopieren. Geht nicht!
    }
    

    Und das in main:

    std::vector<A*> boo;
        boo.push_back(&foo[0]);
        std::cout << boo[0]->getX() << " " << boo[0]->getY() << "  \n";
        wechseln2(boo);
        std::cout << boo[0]->getX() << " " << boo[0]->getY() << " <- Inhalt kopieren geht!  \n";
        std::cout << "foo.size()=" << boo.size() << "\n";
        tuwas(boo);
        std::cout << "nach tuwas: boo.size()=" << boo.size() << " <- wird größer  \n";
        std::cout << boo[1]->getX() << "Inhalt stimmt nicht! \n" ;
    

    Edit: da stand vorhin eine falsche Funktion 😉



  • du übergibst einen pointer auf ein lokales objekt, das ist böse.
    wusste nicht, dass du sowas machen willst. mach einfach einen vector aus A statt aus zeigern auf A. ansonsten musst du wohl oder übel (eher übel) einen allocator machen oder einen vector aus smart_ptr<A> machen. willst du beides nicht da es dir keine vorteile bringt. tl;dr -> std::vector<A>

    "Inhalt stimmt nicht"

    ja, der zeiger zeigt auf eine stelle im speicher an der nichts ist. undefiniertes verhalten. böse.



  • du übergibst einen pointer auf ein lokales objekt, das ist böse.
    

    Ja, deswegen jammer ich schon auf der letzten Seite ... 😃
    Es geht ja um den temporären Speicher. Wenn ich den in main hole und dann in einer Subroutine den Inhalt wechsel, ists kein Problem. Aber ich kann in einer Subroutine nicht die Größe des Arrays ändern ...

    wusste nicht, dass du sowas machen willst. mach einfach einen vector aus A statt aus zeigern auf A.
    

    Nun, warum ich das unbedingt mit Pointern lösen will:
    Die Klasse A hat noch abgeleitete Klassen. Und alle Elemente von A und deren Ableitungen sollen in ein Array rein. Ich möchte mir aber merken, welcher Typ jetzt drin ist. Wenn ich nur das Array über A laufen lassen, ist zwar alles drin, aber der Ableitungstyp geht verloren ...



  • ja dann mach es mit smart pointer:

    #include <iostream>
    #include <vector>
    #include <memory>
    #include <string>
    #include <sstream>
    struct base
    {
    	int a;
    	explicit base(int a = 0)
    		: a(a)
    	{
    	}
    	virtual std::string str() const
    	{
    		std::ostringstream mybuf;
    		mybuf << "i'm a base(" << this->a << ")\n";
    		return mybuf.str();
    	}
    };
    struct derived : base
    {
    	int b;
    	explicit derived(int a = 0, int b = 0)
    		: base(a), b(b)
    	{
    	}
    	virtual std::string str() const
    	{
    		std::ostringstream mybuf;
    		mybuf << "i'm derived(" << this->a << ", " << this->b << ")\n";
    		return mybuf.str();
    	}
    };
    typedef std::vector<std::shared_ptr<base>> container_type;
    void add_base(container_type& vec)
    {
    	vec.push_back(typename container_type::value_type(new base(5)));
    }
    void add_derived(container_type& vec)
    {
    	vec.push_back(typename container_type::value_type(new derived(2, 7)));
    }
    int main()
    {
    	container_type myvec;
    	add_base(myvec);
    	add_derived(myvec);
    	std::cout << "there are " << myvec.size() << " entries, they're:\n";
    	std::cout << myvec[0]->str() << myvec[1]->str();
    }
    

    braucht c++11 support. std::shared_ptr weist relativ intuitives kopier-/zuweisungsverhalten auf. naja nicht ganz, wenn du ein shared_ptr einem anderen zuweist, dann zeigen beide auf das selbe objekt. das zweite objekt ist dann auch noch gültig wenn das erste zerstört wurde (so dass man den besitz über scopes hinweg weitergeben kann). dass ein objekt den fundamentalen regeln zum kopieren usw unterliegt ist anforderung dafür, dass es in stl-containern (z.b. vector) abgelegt werden kann. um das delete kümmert sich shared_ptr selbst.



  • braucht c++11 support. std::shared_ptr weist relativ intuitives kopier-/zuweisungsverhalten auf. naja nicht ganz, wenn du ein shared_ptr einem anderen zuweist, dann zeigen beide auf das selbe objekt. das zweite objekt ist dann auch noch gültig wenn das erste zerstört wurde (so dass man den besitz über scopes hinweg weitergeben kann). dass ein objekt den fundamentalen regeln zum kopieren usw unterliegt ist anforderung dafür, dass es in stl-containern (z.b. vector) abgelegt werden kann. um das delete kümmert sich shared_ptr selbst.

    ganz schön mächtig! 😮
    Da muss ich mich erst einarbeiten, um das Nachzuvollziehen. Vielen Dank! 🙂

    Was genau ist denn davon C++11? Das ganze Prinzip oder nur teilweise einige Funktionen?
    (Mein Compiler kanns zwar, ... interesse! 🙂 )



  • Lymogry schrieb:

    braucht c++11 support. std::shared_ptr weist relativ intuitives kopier-/zuweisungsverhalten auf. naja nicht ganz, wenn du ein shared_ptr einem anderen zuweist, dann zeigen beide auf das selbe objekt. das zweite objekt ist dann auch noch gültig wenn das erste zerstört wurde (so dass man den besitz über scopes hinweg weitergeben kann). dass ein objekt den fundamentalen regeln zum kopieren usw unterliegt ist anforderung dafür, dass es in stl-containern (z.b. vector) abgelegt werden kann. um das delete kümmert sich shared_ptr selbst.

    ganz schön mächtig! 😮
    Da muss ich mich erst einarbeiten, um das Nachzuvollziehen. Vielen Dank! 🙂

    Was genau ist denn davon C++11? Das ganze Prinzip oder nur teilweise einige Funktionen?
    (Mein Compiler kanns zwar, ... interesse! 🙂 )

    jaaa!

    was davon c++11 ist? std::shared_ptr und das ">>" in zeile 34 (wäre in c++03 "> >")



  • hmm .... ich weiß jetzt auch ganz genau wo C++11 ist 😃
    Weil: compiliert nicht! Obwohl es der aktuellste MinGW 4.8.1 in cygwin ist, der theoretisch C++11 kann!
    Theoretisch ... ich sehe im Internet gerade, dass ich nicht allein mit dem Problem bin ... 😕
    Ist jetzt wohl ein Problem für das Compilerforum ... 👎



  • mein code oder dein code?
    denn mein gepostetes codebeispiel funktioniert bei ideone.com mit c++11-einstellung. wenn deiner nicht kompiliert, kannst du ihn mal in der überarbeiteten version posten...



  • Compilerflag std=c++11.



  • Nein, ich wollte deinen Code compilieren.
    Ich suche noch, wie ich MinGW bzw Cygwin einstelle, damit er C++11 kompiliert. Angeblich kennt er es ja ...


  • Mod

    pass auf, gleich kommt sone und sagt dir dass das beides call-by-value ist!

    Nein, der behauptet das Gegenteil, hättest du mal aufgepasst.



  • Nathan schrieb:

    Compilerflag std=c++11.

    AH!
    Ja, Danke Nathan! So gehts! 🙂

    PS für alle, die es auch wissen wollen:
    CYGWIN
    g++ -Wall -pedantic -std=c++11 -o main.exe main.cpp



  • HAHAHA .. guck mal auf was für eine lustige Idee ich gekommen bin: 😃

    #include <iostream>
    #include <vector>
    
    class A {
    public:
        A(const int dx, const int dy) : x(dx), y(dy) {}
        A(const A& einA) :x(einA.x), y(einA.y) {}
        A &operator=(A einA)  {swapi(einA); return *this;}
        void swapi(A& einA);
        int getX() const {return x;}
        int getY() const {return y;}
    
    private:
        int x;
        int y;
    };
    
    void A::swapi(A& einA) {
        std::swap(x,einA.x);
        std::swap(y,einA.y);
    }
    
    void tuwas(std::vector<A*> &foo, std::vector<A> &miststueck) {
        miststueck.push_back(A(2,3));
        foo.push_back(&miststueck[miststueck.size()-1]);
        *(foo[miststueck.size()-1])=miststueck[miststueck.size()-1];
    }
    
    int main() {
        std::vector<A*> foo;
        std::vector<A> miststueck;  // dem klau ich den Speicher!
        A tmp(A(1,1));
        foo.push_back(&tmp);
        std::cout << foo[0]->getX() << " " << foo[0]->getY() << "\n";
        tuwas(foo, miststueck);
        std::cout << foo[1]->getX() << " " << foo[1]->getY() << "\n";
        std::cout << miststueck[0].getX() << " " << miststueck[0].getY() << "\n";
        return 0;
    }
    

    Was sagst du dazu? 🤡



  • lol, gute idee. aber: stichwort: iterator validity

    wenn du den vector veränderst (vergrösserst usw) muss u.U. neuer speicher reserviert werden. dann werden deine alten zeiger ungültig...



  • sfasdfdsf schrieb:

    wenn du den vector veränderst (vergrösserst usw) muss u.U. neuer speicher reserviert werden. dann werden deine alten zeiger ungültig...

    hmmm..... du meinst, der Speicher wäre nicht mehr zusammenhängend?

    Oder meinst du, den vector miststueck darf ich nicht mehr anfassen? (den brauch ich ja dann auch nicht mehr!)



  • nein ich meine damit, dass der vector z.b. 5 elemente gespeichert hat (deren ardresse du nun genommen hast z.b.). wenn du dann plötzlich ein 6tes benötigst (push_back), dann muss sich der vector neuen speicherplatz anschaffen (ausser er hat im voraus reserviert mit vector::reserve()). da der vector aber nur mit zusammenhängendem speicher arbeitet bedeutet das, dass er sich neuen, grösseren speicher holt, den alten dahin in den neuen kopiert und dann den alten löscht, dann werden auch alle zeiger und iteratoren ungültig. dieses problem hast du z.b. mit std::list nicht (dafür ist z.b. zugriff in O(log n)).

    tl;dr -> du solltest es lassen wenn du es nicht unbedingt brauchst.



  • Ah, klar. Das ist nachvollziehbar! 🙂

    Ich kann aber absehen, dass ich maximal 5 push_backs habe!
    Ich hab mal folgendes ausprobiert:

    std::cout << "Kapazitaet: " << miststueck.capacity() << "  ";
        miststueck.reserve(5);
        std::cout << " Kapazitaet: " << miststueck.capacity() << "\n";
    

    (Wenn ich jetzt über die 5 Elemente gehe, kann ich erwarten, dass der Speicher neu allokiert wird, aber drunter ist es sicher, dass er da bleibt wo er ist.)
    Der Anfangswert für "0" capacity() sagt nur, dass noch nix reserviert ist, aber nichts darüber, wieviel Platz eigentlich vorhanden ist. Oder?!

    Noch eine Frage: 🙂
    einmal vector.clear() aufzurufen löscht den Inhalt, aber der Speicher bleibt reserviert, also capacity() bleibt so wie es ist, oder? Bzw. wenn man den Vector neu füllt, füllt er sich exakt an der selben Stelle (es sei denn, man geht mit der Anzahl seiner Elemente drüber, dann muss er u.U. neu allokieren)?

    Merci!
    Ist sehr interessant mit dir 🙂



  • gute idee von dir mit dem reserve()! capacity() gibt in der tat zurück, wie viel speicher intern von vector angefordert wurde. jedoch: reserve() ist ein hinweis an die implementierung. ein hinweis, welcher die implementierung auch ignorieren könnte. wenn du dir also sicher sein willst, mach resize(), hol dir dann deine adressen und lass den vector dann. noch besser, du machst einen const std::vector<T> , initialisierst den mit einer initialiser-list (c++11-feature) und kannst dir dann sicher sein, dass deine adressen für immer richtig sind. wenn du dann die grösse noch zu compilezeit wissen solltest (denke schon, dass es so ist), dann kannst du auch ein std::array nehmen, da man das nicht vergrössern / verkleinern kann, ist es sicher, dass die zeiger und iteratoren immer gültig sind (ausser sie haben eine längere lebensdauer als das std::array selbst).

    ob der speicher erhalten bleibt bei vector::clear ist meinem wissen nach nicht explizit definiert, ergo implemention-defined. das heisst, du darfst dich nicht darauf verlassen wenn du portablen und guten code schreiben möchtest.


Anmelden zum Antworten