Kopierkonstruktor ohne Speicher freigeben?
-
Ordne eine Ressource sofort einem Objekt zu, so lokal wie möglich. Und lass ein Objekt nach Möglichkeit nicht mehrere Ressourcen direkt/manuell verwalten.
ListAllDerives ist nicht Ausnahme-sicher, da Du manuell pcap_freealldevs aufrufen musst, es aber vorher zu einer Ausnahme kommen könnte.
name/description in CDevice sind "owning raw pointers". Pfui. Du kannst dir viel Ärger ersparen, indem Du die Ressourcenverwaltung von CDevice für mehrere Ressourcen (generell schlecht, nimm ein Objekt pro Ressource) eine Ebene tiefer verlagerst:
std::stringstattchar*. Dann kannst du dir auch sparen, Kopierkonstruktor, Zuweisungsoperator und Destruktor von CDevice selbst zu definieren.Warum kompliziert wenn es auch einfach geht?
Schaff dir ein gescheites Buch an, was dir C++ richtig beibringt.
Telefonmann schrieb:
Ich möchte in diesem Fall nur ungern std::string benutzen,

Telefonmann schrieb:
ich will verstehen, warum ich hier einen Kopierkonstruktor brauche
Was ist das denn für eine Aussage?
Ein Kopierkonstruktor ist immer dann nötig, wenn du ein neues Objekt als Kopie erzeugen willst. Ein Zuweisungsoperator ist immer dann nötig, wenn du einem Objekt den "Wert" eines anderen zuweisen willst. Klar oder?
-
Telefonmann schrieb:
Ich wollte eigentlich wissen, warum push_back nicht einfach ein neues Objekt erzeugt...
tut es doch.
Telefonmann schrieb:
Wenn ich das richtig verstanden habe, erzeugt push_back ein neues Objekt mit dem Kontruktorargument und kopiert es anschliessend zu sich selbst...
Das hast du falsch verstanden. Schau dir doch den Kopierkonstruktor nochmal genau an. Dieser funktioniert so:
XXX defaultObject(); // default constructor XXX argumentObject("Hello"); // constructor with arguments XXX copyObject(defaultObject); // copy constructor XXX assignment = argumentObject; // assignment operator
-
daddy_felix schrieb:
XXX defaultObject(); // default constructorDu meinst wahrscheinlich
// Funktionsdeklaration...
-
krümelkacker schrieb:
daddy_felix schrieb:
XXX defaultObject(); // default constructorDu meinst wahrscheinlich
// Funktionsdeklaration...
Bei dir ist der Name aber auch Programm
Das kommt davon, wenn man gerade drei Wochen Urlaub hatte...XXX defaultObject; // default constructor
-
Telefonmann schrieb:
Wenn ich das richtig verstanden habe, erzeugt push_back ein neues Objekt mit dem Kontruktorargument und kopiert es anschliessend zu sich selbst
push_backübernimmt als Parameter nicht den Konstruktorparameter für das neue Objekt, sondern ein vorhandenes Objekt, welches dann in den Vektor kopiert wird. An dieser Stelle tritt die Kopie auf. In deinem Fall funktioniert das trotzdem, weil CDevice einen nicht-explicitKonstruktor besitzt, der einpcap_if_t*als Parameter besitzt, mit diesem kann der Compiler an der StelleaDevices.push_back(myDevice)ein temporäres CDevice-Objekt erzeugen, welches dannpush_backals Parameter übergeben wird.
Seit C++11 unterstütztvectorauf das Einfügen mit direkter Übergabe der Konstruktorparameter für das neu zu erstellende Objekt, womit diese Kopie wegfallen kann. Die Methode dazu heißtemplace_back.Generell sollte man aber immer einen funktionierenden Kopierkonstruktor implementieren (oder diesen zur Not unzugänglich machen), da von C++-Objekten funktionierende Kopiersemantik erwartet wird. Noch besser ist es aber, die Resourcen (insbesondere die Pointer) nicht selbst zu verwalten, dann machen die compilergenerierten Kopierkonstruktoren und Zuweisungsoperatoren schon das richtige und man muss sich darum überhaupt nicht kümmern.
-
@ipsec Jaaa, genau das meinte ich.
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?)
Zu 2) Meinst du eine potentielle Ausnahme bei:
aDevices.clear();?
-
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.
-
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?
-
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...
-
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.