Kopierkonstruktor ohne Speicher freigeben?



  • und selbst dann gibt es immer ncoh sowas wie smart pointer auf arrays, shared_array oder so von boost ist das dann...

    und einfach mal so eine ganze reihe kleiner arrays erstellen hört sich auch nicht ganz sauber an, denke da gibt es dan im konkreten fall bessere alternativen (und sau es um "nur" etwaige ausnahmesicherheiten zu garantieren)



  • Telefonmann schrieb:

    Nur folgendes verstehe ich nicht ganz:

    Ordne eine Ressource sofort einem Objekt zu, so lokal wie möglich.
    ...
    ListAllDerives ist nicht Ausnahme-sicher, da Du manuell pcap_freealldevs aufrufen musst, es aber vorher zu einer Ausnahme kommen könnte.

    Zu 1) Warum sollte ich die Elemente lokal initialisieren? (schon wieder was falsch verstanden?)

    Ich habe mich wahrscheinlich nur blöd ausgedrückt, hatte aber versucht das noch weiter zu beschreiben. Mal an einfachen Beispielen:

    doof: (klasse person muss sich um mehrere Ressourcen selbst kümmern)

    class person
    {
      char* vorname;
      char* nachname;
    public:
      person(const char* vn, const char* nn);
      ~person();
      person(person const&);
      person& operator=(person const&);
    };
    

    schlau: (klasse person kümmert sich um nix. mystring um nur jeweils genau eine Ressource)

    class mystring
    {
      char* data;
    public:
      ~mystring()
      mystring(mystring const&);
      mystring& operator=(mystring const&);
      ...
    };
    
    class person
    {
      mystring vorname;
      mystring nachname;
    public:
      person(const char* vn, const char* nn);
    };
    

    Das meinte ich mit "lokal" ... ist vielleicht nicht das beste Wort dafür, aber ich hoffe, du weißt, wie das gemeint ist.

    ... und statt mystring nimmste einfach std::string aus dem <string>-Header.

    Telefonmann schrieb:

    Zu 2) Meinst du eine potentielle Ausnahme bei:

    aDevices.clear();
    

    ?

    Nee, eher bei den push_backs, die du da machst. Gewöhne dir einfach ab, Ressourcen zu "acquirieren" (Speicher, Datei handles, Locks, etc), ohne dabei die Verantwortung bzgl der Freigabe an ein Objekt abzutreten. Wenn du meinst, Kopierkonstruktor, Zuweisungsoperator und Destrkutor von sehr vielen deiner Klassen selbst definieren zu müssen, hast du echt etwas falsch gemacht, siehe person-Klassenbeispiel.



  • Ah, ok. Habs verstanden. Danke dir 👍



  • Ramanujan schrieb:

    SeppJ schrieb:

    new[] ist niemals eine bessere Alternative zu vector oder string.

    Wirklich niemals? Ich könnte mir Fälle vorstellen, in denen man sehr viele sehr kleine Arrays dynamischer, implizit gegebener Größe benötigt, bei denen der Overhead von vector zu viel Speicher verbrauchen würde. Von daher würde ich sagen, dass new[] fast nie eine bessere Alternative zu vector ist.

    Welchen Overhead gegenüber new[] hat std::vector denn?


  • Mod

    manni66 schrieb:

    Ramanujan schrieb:

    SeppJ schrieb:

    new[] ist niemals eine bessere Alternative zu vector oder string.

    Wirklich niemals? Ich könnte mir Fälle vorstellen, in denen man sehr viele sehr kleine Arrays dynamischer, implizit gegebener Größe benötigt, bei denen der Overhead von vector zu viel Speicher verbrauchen würde. Von daher würde ich sagen, dass new[] fast nie eine bessere Alternative zu vector ist.

    Welchen Overhead gegenüber new[] hat std::vector denn?

    Praktisch hat vector in der Regel die Größe von 3 Pointern, wohingegen bei new ein Pointer und eine Größenangabe (~eine weitere Pointergröße) reicht, sofern man nicht vor hat, das Array zu vergrößern.

    Das Beispiel ist derart konstruiert, dass ich bei nie bleibe. Wenn wir wirklich immer alle möglichen Ausnahmen berücksichtigen, dann kann man in diesem Forum gar nichts mehr sagen, ohne immer ein "fast" in die Aussage aufzunehmen.



  • SeppJ schrieb:

    Wenn wir wirklich immer alle möglichen Ausnahmen berücksichtigen, dann kann man in diesem Forum gar nichts mehr sagen, ohne immer ein "fast" in die Aussage aufzunehmen.

    Man sollte auch fast nie nie schreiben, sondern fast immer fast nie 😃



  • Bitte tötet mich nicht bei meinen ganzen blöden Fragen ...

    Ich versuche gerade eine Klasse in eine andere Klasse einzukapseln. Das problem ist nur, dass die Klasse CEthernet umbedingt Parameter für den Konstruktor braucht. Momentan habe ich dies mit einem Pointer gelöst. Kann man das auch ohne Pointer lösen oder muss das so sein? Ist das in diesem Fall in Ordnung oder sollte man da lieber eine extra Methode zum Einlesen schreiben? (Ich meine soetwas ähnliches schonmal bei QT gesehen zu haben)

    class CPacket
    {
    public:
    	//Methoden
    	CPacket(const struct pcap_pkthdr *pkt_header, const u_char *pkt_data);
    	~CPacket(void);
    	//Elemente
    	CEthernet *eth;
    
    private:
    };
    
    class CEthernet
    {
    public:
    	//Methoden
    	CEthernet(const struct pcap_pkthdr *pkt_header, const u_char *pkt_data);
    	~CEthernet(void);
    	//Elemente
    	u_char dstmac[6], srcmac[6];
    	short frametype;
    };
    
    ...
    
    CPacket::CPacket(const struct pcap_pkthdr *pkt_header, const u_char *pkt_data)
    {
    	//======================================================>
    	//Ethernet-Schicht einlesen (14 Bytes)
    	eth = new CEthernet(pkt_header, pkt_data); //unschön?
    }
    
    CPacket::~CPacket(void)
    {
    	delete eth;
    }
    


  • C-Präfixe kotzen mich an.



  • Telefonmann schrieb:

    Ich versuche gerade eine Klasse in eine andere Klasse einzukapseln. Das problem ist nur, dass die Klasse CEthernet umbedingt Parameter für den Konstruktor braucht.

    Initialisierungsliste



  • Kellerautomat schrieb:

    C-Präfixe kotzen mich an.

    KPacket, KEthernet - besser? 🤡



  • hustbaer schrieb:

    Kellerautomat schrieb:

    C-Präfixe kotzen mich an.

    KPacket, KEthernet - besser? 🤡

    🤡

    Btw: Was hab ich mir eigentlich unter einem "Ethernet"-Objekt vorzustellen?



  • SeppJ schrieb:

    manni66 schrieb:

    Ramanujan schrieb:

    SeppJ schrieb:

    new[] ist niemals eine bessere Alternative zu vector oder string.

    Wirklich niemals? Ich könnte mir Fälle vorstellen, in denen man sehr viele sehr kleine Arrays dynamischer, implizit gegebener Größe benötigt, bei denen der Overhead von vector zu viel Speicher verbrauchen würde. Von daher würde ich sagen, dass new[] fast nie eine bessere Alternative zu vector ist.

    Welchen Overhead gegenüber new[] hat std::vector denn?

    Praktisch hat vector in der Regel die Größe von 3 Pointern, wohingegen bei new ein Pointer und eine Größenangabe (~eine weitere Pointergröße) reicht, sofern man nicht vor hat, das Array zu vergrößern.

    Das Beispiel ist derart konstruiert, dass ich bei nie bleibe. Wenn wir wirklich immer alle möglichen Ausnahmen berücksichtigen, dann kann man in diesem Forum gar nichts mehr sagen, ohne immer ein "fast" in die Aussage aufzunehmen.

    Warum verbraucht ein vector die größe von 3 pointern? Also das ist schon richtig. Aber eigentlich müsste doch ein Pointer für das Datenfeld und eine Größenangabe reichen oder?

    Beim Array in meinem Beispiel wird zusätzlich nur 1 pointer verbraucht, da die Größe ja implizit gegeben ist (d.h. z.B. durch eine Funktion, die einen Index auf die Größe abbildet).

    Aber ich stimme dir voll zu, dass das Beispiel schon sehr konstruiert ist. Und man kann natürlich auch hierbei speichereffizient mit vector arbeiten, indem man einen vector nimmt, in dem alle kleinen Arrays hintereinander gespeichert sind und ein zweiten vector, der die Pointer beinhaltet. Durch die Differenz zweier aufeinanderfolgender Pointer hätte man dann auch Größe quasi explizit gegeben. Wenn man allerdings die Größe eines Arrays in der Mitte ändern möchte, kann man diese Konstruktion schon wieder in die Tonne kloppen...


  • Mod

    Ramanujan schrieb:

    Warum verbraucht ein vector die größe von 3 pointern? Also das ist schon richtig. Aber eigentlich müsste doch ein Pointer für das Datenfeld und eine Größenangabe reichen oder?

    Eben aus diesem Grund programmiert man sich seine Container auch nicht selber, sondern überlässt das denen, die sich damit auskennen. Nur ein Zeiger auf Anfang und eine Größenangabe wären ganz furchtbar ineffizient beim Vergrößern des vectors (also bei einer häufigsten Operationen überhaupt). Es wäre mWn nicht einmal möglich, damit die gemittelt konstante Laufzeitanforderung für ein push_back zu erfüllen, sondern ein push_back wäre mindestens linear im Zeitaufwand. Üblich sind daher: Zeiger auf Anfang, Zeiger auf Ende der Daten, Zeiger auf Ende des reservierten Bereichs.



  • Ja, stimmt, ich hatte in dem Moment vergessen, dass die Größe des reservierten Feldes mitgespeichert werden muss.



  • Es war einmal ein Programm, das rechnete viel mit 3D Vektoren. Vektoren aus jeweils drei float s. std::vector<float> Vektoren aus jeweils drei float s 😮
    Es wurde dann umgeschrieben, dann war es schneller und brauchte weniger Speicher, und alle waren froh. (Später wurde es dann eingestampft und komplett neu entwickelt, weil es immer noch grässlich war, aber das ist eine andere Geschichte und soll ein andermal erzählt werden)

    Das ist nicht konstruiert und nicht gelogen.
    (Dafür passt es nicht wirklich zu dem was hier eigentlich diskutiert wird, aber hey, man kann nicht alles haben :D)

    (ps. das Programm war nicht von mir, ich durfte es nur bewundern!)



  • Kellerautomat schrieb:

    Btw: Was hab ich mir eigentlich unter einem "Ethernet"-Objekt vorzustellen?

    Vermutlich etwas was lieber eine struct werden wollte, und die Daten speichert die in der Ethernet-Header so drinstehen (bzw. wenigstens ein paar davon).



  • Telefonmann schrieb:

    Bitte tötet mich nicht bei meinen ganzen blöden Fragen ...

    Oh ... ich versuch's mal ...

    Telefonmann schrieb:

    Ich versuche gerade eine Klasse in eine andere Klasse einzukapseln. Das problem ist nur, dass die Klasse CEthernet umbedingt Parameter für den Konstruktor braucht. Momentan habe ich dies mit einem Pointer gelöst. Kann man das auch ohne Pointer lösen oder muss das so sein?

    Das kann man auch ohne Pointer lösen. Das Stichwort fiel ja auch schon: Initialisierungsliste. Bitte besorg dir ein gescheites C++ Grundlagenbuch. Es wird allen Ärger ersparen, dir und uns.

    Telefonmann schrieb:

    Ist das in diesem Fall in Ordnung

    Meinst du mit "das" die Verwendung eines rohen Zeigers? Nee, nicht wirklich.

    Telefonmann schrieb:

    class CPacket
    {
    public:
    	//Methoden
    	CPacket(const struct pcap_pkthdr *pkt_header, const u_char *pkt_data);
    	~CPacket(void);
    	//Elemente
    	CEthernet *eth;
    
    private:
    };
    
    class CEthernet
    {
    public:
    	//Methoden
    	CEthernet(const struct pcap_pkthdr *pkt_header, const u_char *pkt_data);
    	~CEthernet(void);
    	//Elemente
    	u_char dstmac[6], srcmac[6];
    	short frametype;
    };
    
    ...
    
    CPacket::CPacket(const struct pcap_pkthdr *pkt_header, const u_char *pkt_data)
    {
    	//======================================================>
    	//Ethernet-Schicht einlesen (14 Bytes)
    	eth = new CEthernet(pkt_header, pkt_data); //unschön?
    }
    
    CPacket::~CPacket(void)
    {
    	delete eth;
    }
    

    Mir ist schleierhaft, warum du in CEthernet einen Destruktor deklarierst. Außerdem verletzt du bei CPacket die Dreierregel. Besorg dir bitte ein schlaues C++ Buch.


Anmelden zum Antworten