std:vector push_back deconstruiert meine Elemente



  • Ich habe eine vector Liste der Klasse PhaseObject

    vector<PhaseObject> PhaseObjects;
    

    diese Klasse enthält unter anderem das Element

    public:
    	beamshapes ComplexBeam;
    

    welches abgeleitet ist von 'ComplexBeam'

    class beamshapes : public ComplexBeam
    

    welches drei dynamische Arrays enthält.

    Wenn ich nun ein Objekt von PhaseObject, hier FresnelPhaseObject, zum Vector PhaseObjects hinzufüge:

    PhaseObjects.push_back(FresnelPhaseObject);
    

    dann wird dabei der Decontructor von PhaseObject und damit auch von ComplexBeam aufgerufen. Dabei werden alle dynamischen Arrays gelöscht.

    Das Vector Element enthält damit nur noch dekonstruierte Pointer und jeder zugriff auf das Element lässt die Applikation abstürzen.

    was läuft da falsch?
    Ich bin leider schon sehr verweifelt, da ich nicht mehr weiß nach was ich noch suchen soll.

    Matthias



  • Du musst den Copy-Constructor ordentlich überladen (bzw alle "großen drei", Copy-Constr, Zuweisungs-Op und Destruktor).



  • Badestrand schrieb:

    Du musst den Copy-Constructor ordentlich überladen (bzw alle "großen drei", Copy-Constr, Zuweisungs-Op und Destruktor).

    Das sagt mir erstmal gar nichts. Kannst du mir dazu weiter Hinweise / Dokus geben?



  • pospiech schrieb:

    Das sagt mir erstmal gar nichts. Kannst du mir dazu weiter Hinweise / Dokus geben?

    Ist im Prinzip simpel: Wenn du mit push_back ein Element hinzufügst, muss u.U. (falls der vector nicht genug Speicher reserviert hat) neuer/größerer Speicher allokiert werden. Dazu werden die alten Elemente rüberkopiert (auf dem neuen Speicher wird für jedes alte Element der Copy-Constructor auf dem entsprechenden neuen Speicherstück aufgerufen, mit Parameter das Objekt auf dem alten Speicher). Dann wird der alte Speicher de-allokiert, vorher aber noch für jedes Element darauf der Destruktor aufgerufen, damit die Objekte wissen, dass sie weg sind.
    Copy-Constructoren haben immer folgende Form:

    class xyz
    {
        xyz( const xyz& other );
    };
    

    Um das Verhalten vom vector bei push_back nochmal deutlicher zu machen:

    Falls noch genug Speicher reserviert ist:
        neues Element hinzufügen
    
    Sonst:
        Speicher allokieren, wo auch das neue Element reinpasst
        Auf den neuen Speicher alle Objekte des alten Speichers rüberkopieren, mit Copy-Constructor-Aufruf (Fachwort placement-new)
        Für alle Objekte auf dem alten Speicher den Destruktor aufrufen
        Den alten Speicher wieder freigeben
    


  • Achso, und das Problem bei dir ist wahrscheinlich so:

    class Data
    {
        public:
            Data()
            {
                m_ptr = new char [100]; // oder so
            }
    
            ~Data()
            {
                delete[] m_ptr;
            }
    
        private:
            char* m_ptr;
    };
    

    Der Compiler generiert immer automatisch einen Copy-Constructor, wenn du das nicht machst. Darin werden dann einfach alle Member-Variablen der Klasse kopiert, die neue Instanz von "Data" hat dann also den selben Zeiger. Die alte Instanz von Data wird destruiert, dabei wird delete[] auf den Zeiger angewendet. Die neue Instanz hat den Zeiger aber noch und denkt, er wäre gültig. Das ist dann halt das Dilemma 😉



  • Badestrand schrieb:

    Ist im Prinzip simpel: Wenn du mit push_back ein Element hinzufügst, muss u.U. (falls der vector nicht genug Speicher reserviert hat) neuer/größerer Speicher allokiert werden. Dazu werden die alten Elemente rüberkopiert (auf dem neuen Speicher wird für jedes alte Element der Copy-Constructor auf dem entsprechenden neuen Speicherstück aufgerufen, mit Parameter das Objekt auf dem alten Speicher). Dann wird der alte Speicher de-allokiert, vorher aber noch für jedes Element darauf der Destruktor aufgerufen, damit die Objekte wissen, dass sie weg sind.

    Really? I always thought that std::vector just copy memory byte-by-byte to the new location without calling copy-constructors and destructors. Such memory copying will significantly speed-up adding of new element and will avoid the case you mentioned in the last post (where absence of copy-constructor cause program error), because the buffer m_ptr points to will remain the same. The only limitation of such approach is that subobjects must not have pointers to the parent object.



  • SAn schrieb:

    Really? I always thought that std::vector just copy memory byte-by-byte to the new location without calling copy-constructors and destructors.

    Alle Container aus der Standardbibliothek die mit Objekten arbeiten, benutzen auch den Copy-Konstruktor und Destruktor. Wobei sie hier ggf. über den placement new Operator gehen (Container- und Implementierungsabhängig).

    cu André



  • Ich habe mich mal an einem copy Konstruktor versucht. Aber den kann ich nicht mit einem const versehen, dann compiliert es nämlich nicht.

    ComplexBeam::ComplexBeam(ComplexBeam & newBeam) 
    {
    	m_Dimensions = newBeam.Dimensions();
    	m_DataSize = newBeam.Size();
    	switch (m_Dimensions)
    	{
    		case 1: setSize(m_DataSize.width()); break;
    		case 2: setSize(m_DataSize.width(), m_DataSize.height()); break;
    	}
    	newBeam.DataComplex(m_ComplexBeamData.Complex);
    	m_DataWithPhase = false;
    	m_DataWithAmplitude = false;
    }
    

    wie müssten den die anderen Operatoren aussehen?



  • pospiech schrieb:

    Ich habe mich mal an einem copy Konstruktor versucht. Aber den kann ich nicht mit einem const versehen, dann compiliert es nämlich nicht.

    Ach so, dann ist ja alles klar. Solltest du es inzwischen nicht so langsam mal mitbekommen haben, wie wichtig Fehlermeldungen sind?

    Vermutlich ist ComplexBeam::Dimensions oder ComplexBeam::Size oder ComplexBeam::DataComplex nicht const.

    Die Namensgebung "newBeam" ist übrigens unglücklich. Der Parameter ist das zu kopierende, also das "alte" Objekt. *this ist das neue.

    pospiech schrieb:

    wie müssten den die anderen Operatoren aussehen?

    denn!

    http://www.gotw.ca/gotw/059.htm



  • MFK schrieb:

    pospiech schrieb:

    Ich habe mich mal an einem copy Konstruktor versucht. Aber den kann ich nicht mit einem const versehen, dann compiliert es nämlich nicht.

    Ach so, dann ist ja alles klar. Solltest du es inzwischen nicht so langsam mal mitbekommen haben, wie wichtig Fehlermeldungen sind?

    Vermutlich ist ComplexBeam::Dimensions oder ComplexBeam::Size oder ComplexBeam::DataComplex nicht const.

    ne const war gar keine, weil ich das noch nie gebraucht habe. Aber anscheint kann ich nicht alle zu const machen.

    void ComplexBeam::DataComplex(complex<double>* OutputArray) const
    {
    	memcpy(OutputArray,m_ComplexBeamData.Complex,DataSizeComplex());
    

    liefert mir die Fehlermeldung

    1>.\src\complexbeam.cpp(374) : error C2662: 'ComplexBeam::DataSizeComplex': this-Zeiger kann nicht von 'const ComplexBeam' in 'ComplexBeam &' konvertiert werden
    1> Durch die Konvertierung gehen Qualifizierer verloren



  • Na dann mach doch ComplexBeam::DataSizeComplex auch const. Das solltest du mit allen Methoden tun, die dein Objekt nicht verändern.


Anmelden zum Antworten