std::map im Copy-Constructor überschreibt Daten
-
Ich bin gestern auf ein mir unerklärliches Problem gestoßen. Ich habe ein Design der folgenden Art:
class A { private: B b; public: B getB() { return b; } }; class B { private: std::map<int, float*> map; public: B(B &b); // copy ctor B &operator =(B &b); // assignment operator }; foo(const A &a) { float f = 0.0f; B b = a.getB(); }Wenn in foo() nun die zweite Zeile ausgeführt wird, dann wird der Wert von f mit Müll überschrieben und beim Aussteigen aus der Funktion gibt es den Fehler, dass um f der Stack korrumpiert sei (völlig zurecht). Ich habe das Überschreiben genau auf die Initialisierung der map im Copy-Constructor von B, der in getB() aufgerufen wird, zurückverfolgt. Nun liegt in foo() b im Speicher genau vor f, und da scheint irgendwie aus b hinten was überzulaufen und f zu überschreiben.
Das ist mir wie gesagt völlig unverständlich. Ein Objekt kann ja nicht einfach seine Größe verändern, und die Map dürfte auch ihre Daten nicht einfach so in schon benutztem Speicher ablegen.
Ich habe das Problem vorerst umgangen, indem ich b auf dem Heap allokiere, aber das kommt mir wie ein übler Workaround vor. Ich würde gerne wissen, warum sich das hier so verhält.
-
Zeig doch mal bitte deine Implementierung des copy-CTors und des operator=.
Du übergibst denen nicht-const-Referenzen. Ist eigentlich unnötig, willst ja das kopierte Objekt (hoffentlich) nicht verändern.
-
Das Beispiel sollte eigentlich in Zeile 18 einen Compilerfehler verursachen, weil kein passender Copy-ctor existiert (wobei nicht alle Compiler das im Falle von copy-elision überprüfen).
-
@camper: Der normale Copy-Ctor wird doch immer noch generiert. Ein Konstruktor, der eine non-const Referenz nimmt, gilt imho als normaler Konstruktor und nicht als Kopierkonstruktor. Der Kompiler erstellt also keinen Default-Ctor. Den Kopierkonstruktor erzeugt er aber noch.
-
Don06 schrieb:
@camper: Der normale Copy-Ctor wird doch immer noch generiert. Ein Konstruktor, der eine non-const Referenz nimmt, gilt imho als normaler Konstruktor und nicht als Kopierkonstruktor. Der Kompiler erstellt also keinen Default-Ctor. Den Kopierkonstruktor erzeugt er aber noch.
12.8/2
A non-template constructor for class X is a copy constructor if its first parameter is of type X&, const X&, volatile X& or const volatile X&, and either there are no other parameters or else all other parameters have default arguments (8.3.6).106) [Example: X::X(const X&) and X::X(X&, int=1) are copy constructors.
-
@camper: Oh, das wusste ich nicht. Aber ist dann der Fehler im obigen Fall nur durch einen Compiler-Fehler zu erklären?
Ich habe mal ein lauffähiges Programm erstellt. Der Fehler tritt bei mir nicht auf.
#include <map> #include <iostream> class B { private: std::map<int, float*> map; public: B() {} B(const B &b) // copy ctor : map(b.map) { } }; class A { private: B b; public: B getB() const { return b; } }; void foo(const A &a) { float f = 0.0f; B b = a.getB(); std::cout << f; } int main() { foo(A()); }
-
Hallo David Schneider,
der Fehler scheint nicht direkt in dem gezeigten Code zu liegen. Allerdings fällt auf das Du einen Zeiger auf float in der Map speicherst. Da gibt es viele mögliche Fehlerquellen. Kann es sein, das Du in einer anderen Funktion sinngemäß folgendes machst?
void bar(B &b) { float f = 0.0f; b[5] = &f; }
-
DJohn@work schrieb:
...Allerdings fällt auf das Du einen Zeiger auf float in der Map speicherst. ...
Das ist mir auch als Erstes aufgefallen.
Eine andere dadurch entstehende Fehlerquelle hat bereits "Kopist" angemerkt: Klassen mit Zeigern zu kopieren ist nicht trivial - und es wird bei std::map garantiert reichlich genutzt.
Dieses Problem löst man übrigens nicht durch Ignorieren (weder durch mentales noch durch programmiertechnisches
).Gruß,
Simon2.
-
Simon2 schrieb:
Dieses Problem löst man übrigens nicht durch Ignorieren (weder durch mentales noch durch programmiertechnisches
).Weswegen es dieses Topic gibt.
Ich habe das Problem aber durch eine neue Gestaltung des verwendenden Codes beseitigen können. Ich weiß es nicht mit Sicherheit, aber ich vermute den Fehler bei meinem eigenen Code in der Klasse, der eben jene Zeiger auf floats in der Map mit Arrays füllte und diese wieder löschte. Gut möglich, dass dabei eine Unvollständigkeit passiert ist.