Konstruktor eigener Klasse und std::vector Initialisierung - Verständnisproblem
-
Hallo :),
entschuldigt den womöglich etwas seltsam anmutenden Threadtitel, aber mir fiel keine bessere Kurzbeschreibung zu folgender Sache ein. Ich poste erstmal den zu meinem Verständisproblem dazugehörigen (und für diesen Beitrag auf die wichtigsten Elemente gekürzten) Code.
Hier die Klasse:
// lokalitaet.h class Lokalitaet { public: // Konstruktor Lokalitaet(const std::string &l_name = "Neue Lokalitaet", const std::string &l_beschr = "Leer"): lok_name(l_name), lok_beschreibung(l_beschr) { lok_id = lok_nummer++; } // Operatoren friend std::ostream& operator<<(std::ostream &os, const Lokalitaet &lok); // Getter, Setter, etc. private: std::string lok_name; std::string lok_beschreibung; size_t lok_id; static size_t lok_nummer; };Und die entsprechenden Definitionen:
// lokalitaet.cpp #include "lokalitaet.h" // Staticelement size_t Lokalitaet::lok_nummer = 0; // Operatoren std::ostream& operator<<(std::ostream &os, const Lokalitaet &lok) { os << lok.lok_name << "(" << lok.lok_id << "): " << lok.lok_beschreibung; return os; }Wie man sieht soll der Konstruktorrumpf dafür sorgen, dass jedes "Lokalitaet"-Objekt seine eigene ID erhält. Wenn ich jetzt in die
main()folgendes schreibe, klappt das auch wunderbar.//main.cpp #include "lokalitaet.h" /* weitere #include-Befehle, sowie using-Deklarationen */ int main() { vector<Lokalitaet> orte; orte.push_back(Lokalitaet("Test1", "abc")); orte.push_back(Lokalitaet("Test2", "xyz")); // Ausgabe aller Vectorelemente for(vector<Lokalitaet>::const_iterator it = orte.begin(); it != orte.end(); ++it) cout << *it << endl; return 0; }Wenn ich das ganze kompiliere erscheint auch die von mir erwartete Ausgabe (man beachte die verschiedenen ID's die in Klammern stehen).
Test1(0): abc Test2(1): xyzNachdem das erstmal alles geklärt ist kommt hier nun die Sache, die ich nicht so ganz verstehe, denn wenn die
main()folgendermaßen aussieht://main.cpp #include "lokalitaet.h" /* weitere #include-Befehle, sowie using-Deklarationen */ int main() { vector<Lokalitaet> orte(5, Lokalitaet("Test", "abc")); // Ausgabe aller Vectorelemente for(vector<Lokalitaet>::const_iterator it = orte.begin(); it != orte.end(); ++it) cout << *it << endl; return 0; }Dann erscheint die für mich nicht nachvollziehbare folgende Ausgabe:
Test(0): abc Test(0): abc Test(0): abc Test(0): abc Test(0): abcJedes Vectorelement hat für mich unverständlicherweise die selbe ID (nämlich die Zahl die in Klammern steht, also die 0). Ich hoffe jemand könnte mir bitte erklären wieso beim anlegen eines nicht leeren Vectors der Konstruktor meiner Klasse offenbar nur einmal aufgerufen wird. Vielen Dank schonmal im voraus dafür und auch vielen Dank für das lesen meines doch etwas länger geratenen Beitrages :).
-
Also die Referenz erklärt dir das Problem meines Erachtens:
http://www.cplusplus.com/reference/stl/vector/vector.htmlund dann das hier:
explicit vector ( size_type n, const T& value= T(), const Allocator& = Allocator() );
Repetitive sequence constructor: Initializes the vector with its content set to a repetition, n times, of copies of value.
-
Achso, also wird praktisch in meinem Fall ein "Lokalitaet"-Objekt erstellt und dieses dann n-mal kopiert. Ok, damit habe ich wieder was dazugelernt. Vielen Dank Firefighter :).
-
Genau das ist das Problem. Ich sitze jetzt auch schon ne Weile um das kopieren zu umgehen, aber ich weiß selber nicht wie und ob es überhaupt möglich ist, dein gewünschtes Verhalten trotz der Verwendung des Ctors wie du ihn gepostet hast in der Main. Vielleicht weiß da jemand anderes Rat.
-
Auch wenn es ich es nicht ganz so schön finde werde ich wohl erstmal die "Lokalitaet"-Objekte mittels
push_backzum Vector hinzufügen, was jetzt auch nicht so schlimm ist, dennoch wäre auch ich an einer möglichen anderen Lösung interessiert :).
-
Du könntest den Kopierkonstruktor so überladen, dass auch Aufrufe von ihm in einer Erhöhung des Zählers resultieren.
Lokalitaet::Lokalitaet(const Lokalitaet& Origin) : lok_name(Origin.lok_name) , lok_beschreibung(Origin.lok_beschreibung) , lok_id(lok_nummer++) { }Du musst dir allerdings auch überlegen, was du bei Zuweisungen machst. Entweder du verbietest sie ganz, was aber die Verwendung in Containern erschweren kann, oder du erhöhst auch dann die ID. Da innerhalb von Containern oft Kopien erzeugt werden können, ist die ID nicht mehr wirklich repräsentativ. Temporäre Objekte erhöhen den Counter ja ebenfalls. Darum kann es gut vorkommen, dass z.B. nach einer Reallokation von
std::vectorplötzlich zehn Nummern übersprungen werden.Eine relativ einfache Möglichkeit, eindeutige IDs zu bewahren, stellen Smart Pointer dar.
boost::shared_ptroderstd::tr1::shared_ptr(identisch) kann in Containern verwendet werden und stellt sicher, dass bei Kopien kein neues Objekt entsteht. Siehe dazu auch diesen Artikel.
-
An Smart Pointer hatte ich noch gar nicht gedacht und das obwohl ich dazu bereits einiges im "C++ Primer" erfahren hatte. Es wird wohl mal Zeit mich intensiver damit auseinanderzusetzen. Vielen Dank Nexus für die Hilfestellung :).