vector hinzufügen ohne objekt zu kopieren (ggf. refcount++)
-
Nein. Nicht leer. Sonder einfach gar nicht definieren.
class TElement { private: TElement(const TElement& e); ...frank schrieb:
da bekomme ich nen Fehler
platine.h: In member function ‘bool TPlatine::OnRedraw(GdkEventExpose*)’:
platine.h:210: error: ‘TElement::TElement(const TElement&)’ is private
platine.cpp:555: error: within this context
platine.cpp:555: error: initializing argument 1 of ‘void TPlatine::DrawElement(TElement)’zeile 210 ist die definition des copy-CTors siehe oben
Wie ich vermutet habe, wird versucht, ein TElement-Objekt zu kopieren. Wenn so ein Objekt aber einen unkopierbaren vector enthält (weil er zB unique_ptr-Objekte speichert), dann geht das natürlich nicht.
frank schrieb:
evtl ist in Zeile 555 schon der Hund begraben...
hier der code-Ausschnitt:
for (unsigned int i=0; i<pElements.size();i++) { DrawElement(*pElements[i]); //zeile 555 }Anhand der Fehlermeldung kann ich entnehmen, dass Du DrawElement so deklariert hast:
void DrawElement(TElement);und die hätte vielleicht so aussehen sollen:
void DrawElement(TElement const&);
-
Habe das soweit eingebaut
void DrawElement(TElement const &e);platine.cpp: In member function ‘void TPlatine::DrawElement(const TElement&)’:
platine.cpp:259: error: passing ‘const TElement’ as ‘this’ argument of ‘TRect TElement::GetRect()’ discards qualifiers259: TRect r=e.GetRect();GetRect ist eine public-methode, welche interne variablen zurückgibt...sollte imho mit const-pointer auch funktionieren
wenn ich das const in DrawElement weglasse, kommt kein Fehler. Weis nur nicht, ob das so sein soll
durch das & wird jedoch das TElement nicht kopiert, sondern nur der pointer der Funktion übergeben, aus meiner Sicht richtig
-
ich habe die pConnectionList wieder eingebaut und bisher funktioniert alles (also es compiliert
)...herzlichen Dank an alle, die mir geholfen haben
-
frank schrieb:
GetRect ist eine public-methode, welche interne variablen zurückgibt...sollte imho mit const-pointer auch funktionieren
Sollte. Dazu musst du die Funktion aber auch bei der Deklaration entsprechend markieren mit einem const. Beispiel:
class A { int z; public: A() : z(0) {} void dings() {++z;} int bums() [b]const[/b] {return z;} };frank schrieb:
wenn ich das const in DrawElement weglasse, kommt kein Fehler. Weis nur nicht, ob das so sein soll

Nein. Das war die falsche Stelle.
frank schrieb:
durch das & wird jedoch das TElement nicht kopiert, sondern nur der pointer der Funktion übergeben, aus meiner Sicht richtig
so ähnlich.
Du schreibst einfach so C++ Code hin, ohne zu wissen, was er genau bedeutet? Mit der Strategie fällst Du sehr schnell auf die Klappe bei C++. Dir fehlt da so einiges an Grundlage. Schaff Dir mal ein schlaues Buch an und lerne...
-
krümelkacker schrieb:
Sollte. Dazu musst du die Funktion aber auch bei der Deklaration entsprechend markieren mit einem const. Beispiel:
class A { int z; public: A() : z(0) {} void dings() {++z;} int bums() [b]const[/b] {return z;} };wenn ich die funktion als const definiere, kann darin nichts verändert werden. bei der GetRect muss es auch nicht, aber irgendwann brauche ich auch den schreibenden Zugriff auf Elemente des übergebenen Objektes.
was ist denn so schlimm, wenn ich das const weglasse? die referenz selbst kann ich doch nicht ändern, sondern nur das Objekt dahinter...und das will ich ja
krümelkacker schrieb:
so ähnlich.
Du schreibst einfach so C++ Code hin, ohne zu wissen, was er genau bedeutet? Mit der Strategie fällst Du sehr schnell auf die Klappe bei C++. Dir fehlt da so einiges an Grundlage. Schaff Dir mal ein schlaues Buch an und lerne...
habe hier das "Einstieg in c++" von arnold willemer. das lese ich nebenbei durch.
mit dieser ganzen Pointergeschichte begehe ich nur Neuland, welches unter c++ komplizierter ist als in anderen Programmiersprachen (wie aus meinem Komplett-Code ersichtlich ist, versuche ich ein Delphi-Programm zu portieren)ok, Zeiger und Referenz sind unter c++ etwas anderes und doch fast gleich, das ist mir entfallen und beim const kommt es auch darauf an, wo es genau steht.
-
frank schrieb:
wenn ich die funktion als const definiere, kann darin nichts verändert werden.
Es würde auch keiner erwarten, dass eine Funktion namens "DrawElement" das übergebene Objekt modifiziert. Um genau das auszudrücken, wird const verwendet.
frank schrieb:
bei der GetRect muss es auch nicht, aber irgendwann brauche ich auch den schreibenden Zugriff auf Elemente des übergebenen Objektes.
bei "DrawElement" ?!
frank schrieb:
was ist denn so schlimm, wenn ich das const weglasse?
Wenn Du es bei GetRect weglässt, kannst Du GetRect nur auf einem Objekt aufrufen, wenn Du auch "Schreibzugriff" auf das Objekt hast. Aber warum solltest Du Schreibzugriff haben müssen, nur um das Objekt nach etwas fragen zu können?
frank schrieb:
die referenz selbst kann ich doch nicht ändern, sondern nur das Objekt dahinter...und das will ich ja
Wo?
-
krümelkacker schrieb:
frank schrieb:
was ist denn so schlimm, wenn ich das const weglasse?
Wenn Du es bei GetRect weglässt, kannst Du GetRect nur auf einem Objekt aufrufen, wenn Du auch "Schreibzugriff" auf das Objekt hast. Aber warum solltest Du Schreibzugriff haben müssen, nur um das Objekt nach etwas fragen zu können?
ich meinte eigentlich das const beim DrawElement

krümelkacker schrieb:
frank schrieb:
die referenz selbst kann ich doch nicht ändern, sondern nur das Objekt dahinter...und das will ich ja
Wo?
bisher noch nicht, da ich noch keine Setter-Methoden groß drin habe...
Aber meine Befürchtung ist, dass ich irgendwann eine solche Methode habe, wo ich schreibzugriff habe (und brauche) und dann eine solche const-funktion aufrufen muss...
wenn ich da keine Probleme (lässt sich evtl. nur mit const-element aufrufen) bekomme ists ok
nochmals danke, dass du dir so viel Zeit für meine Probleme nimmst...allein auf die Idee mit dem Copy-Constructor zur Fehlersuche wäre ich nicht gekommen.
-
eine Frage dazu hätte ich noch...
wie sähe eine Getter-Methode für die unique_ptr<TElement> aus?
unique_ptr<TElement> GetElement(/*std::string name*/ int index) const {return pElements[index];};und
TElement& GetElement(/*std::string name*/ int index) const {return pElements[index];};funktionieren nicht...
-
Bevor du einfach irgendwas ausprobierst, mach dich mit der Materie vertraut. Schau dir das Konzept von Zeigern genau an (z.B. wann du Dereferenzierung brauchst). Anschliessend schaust du dir die Semantik von
unique_ptran und merkst, dass der Besitz transferiert wird, es also relativ sinnfrei ist, eineconst-Methode einenunique_ptrzurückgeben zu lassen.
-
ok, da ich weder unique_ptr zurückgeben noch TElement kopieren kann, habe ich es mal so probiert:
TElement& GetElement(/*std::string name*/ int index) const {return *pElements[index];};funktioniert auch mit folgendem Code:
TElement &e=platine.GetElement(idx); e.Rotate(false);müsste doch stimmen, oder?
habt ihr eine gute (möglichst deutsche) dokumentation zu unique_ptr? was ich so über google gefunden habe, war nicht wirklich verständlich für mich. mein Buch behandelt die STL nur rudimentär und die Smart-Pointer (unique_ptr/shared_ptr/auto_ptr) gar nicht.
-
Ich habe den Eindruck, dass Du zu wenig von C++ verstehst, als dass dein aktuelles Design irgendwie etwas taugen würde. Beispielsweise zweifel ich ganz stark daran, dass an dieser Stelle der Einsatz von unique_ptr überhaupt sinnvoll ist.
-
krümelkacker schrieb:
Ich habe den Eindruck, dass Du zu wenig von C++ verstehst, als dass dein aktuelles Design irgendwie etwas taugen würde.
ich bin noch Anfänger (ist mein 3. größeres Projekt) in C++. ich bin nur hobbymäßiger Programmierer, der nur aus Büchern und dem Netz lernt. C++ verwende ich aktiv seit ca. 2 Jahren. Programmieren allgemein seit ca. 14 Jahren.
weiterhin stammt das Design aus einer anderen Programmiersprache (Delphi). und dieses Design möchte so gut wie möglich 1:1 adaptieren, um die Pflege beider Code-Repos so einfach wie möglich zu halten.
Prinzipell brauche ich
- eine Liste von TElement/TConnection (TList unter delphi => einfache Pointerliste, wo man sich selbst um das Casten und die Speicherverwaltung der Elemente kümmern muss)
- die Elementliste befindet sich in TPlatine
- in TElement befindet sich eine TConnectionList
- dabei muss eine TConnection immer wissen zu welchem TElement sie gehört
- ich möchte von außerhalb (Hauptprogramm) auf die TElement/TConnection-Objekte zugreifen können (lesen und verändern).
- als beste Variante habe ich vector aus der STL verstanden, da ich mich dann nicht um die freigabe der Elemente kümmern muss und die Liste dynamisch wachsen kann, ohne dass ich mich um deren Speicherverwaltung kümmern muss (wie ich es bei einem dynamischen array z.b. müsste)
- standardmäßig kopiert vector das element beim hinzufügen => Pointer auf TElement wird ungültig (Objekt hinter dem Pointer ist ja lokal und wird freigegeben)
- und mit diesem Vorwissen ist dieser Thread entstanden...krümelkacker schrieb:
Beispielsweise zweifel ich ganz stark daran, dass an dieser Stelle der Einsatz von unique_ptr überhaupt sinnvoll ist.
unique_ptr wurde mir in diesem Thread vorgeschlagen (gut, ich habe nicht alle Einzelheiten meines Vorhabens erwähnt)
wie im letzten Beitrag schon erwähnt sind meine STL-Kenntnisse rudimentär und von unique_ptr (aufgrund fehlender verständlicher Dokumentation) so gut wie gar nicht vorhanden. Was mir bleibt ist nur probieren bzw. anhand eurer Antworten und meinem bisherigem Wissen das umzusetzen (wie der Hinweis mit der Dereferenzierung). Warum ist mein Ansatz denn falsch?
Wenn es Alternativen gibt bin ich offen. vielleicht gibts es auch Möglichkeiten ohne vector...ich hoffe, ich habe einen Einblick in das verschafft, was ich (nicht) weis und was ich erreichen will.
-
da aktuell einige Threads sich mit Pointerlisten beschäftigen...
wäre das nicht eine sinnvolle Option für mein Problem?
so wie ich mir das vorstelle:
vector<TElement*> pElements; void TPlatine::AddElement(...) { TElement* pE = new TElement(...); pElements.push_back(pE); } void TPlatine::DelElement(int index) { delete pElements[index]; pElements.erase (pElements.begin()+index); } TPlatine::~TPlatine() { for (int i=0; i<pElements.size(); ++i) delete pElements[i]; } TElement* TPlatine::GetElement(int index) { if ((index >=0) && (index <pElements)) return pElements[index]; else return NULL; }wäre das so ok?
-
Ist ok, wenn du eben falls Copy-Ctor und copy-assignment operator= überlädst.
-
Nein. Das ist keine gute Lösung. Das kann man ganz unabhängig davon sagen, was das ganze werden soll; denn es ist eine manuelle Speicherverwaltung, die Du Dir mit vector<unique_ptr<TElement>> oder boost::ptr_vector<TElement> hättest sparen können.
-
@krümelkacker
ich will halt nur ungern etwas machen, wo ich Null-Dokumentation da habe und es daher so gut wie nicht verstehe...meine Herauslösung von TElement war ja auch falsch, ob wohl es funktioniert.und du hast selbst geschrieben, dass du bezweifelst, dass unique_ptr bei meinem Vorgehen sinnvoll ist...
daher habe ich nach Alternativen gesucht. und auf diese Weise habe ich es in dem Original-Programm (Delphi) gemacht (ohne Speicherlecks).
@ethon
wozu soll ich den Copy-Ctor und = überladen, wenn ich die Elemente nur mit den hier geposteten Methoden bearbeite? Außerhalb der TPlatine-Klasse wird kein Telement erstellt oder freigegeben. das holen der TElement-Objekte läuft nur über Pointer, wo danach auch das Objekt nicht automatisch freigegeben wird (Zugriff dann nur per ->).ich persönlich habe nichts gegen eine manuelle Speicherverwaltung, wenn der Speicher an den Richtigen Stellen (Destructor der Liste bzw. bei mir TPlatine) UND beim löschen von Elementen freigegeben wird und gleichzeitig der/die Pointer auf das Element (nur in der Liste) vernichtet wird.
-
@ethon
wozu soll ich den Copy-Ctor und = überladen, wenn ich die Elemente nur mit den hier geposteten Methoden bearbeite? Außerhalb der TPlatine-Klasse wird kein Telement erstellt oder freigegeben. das holen der TElement-Objekte läuft nur über Pointer, wo danach auch das Objekt nicht automatisch freigegeben wird (Zugriff dann nur per ->).ich persönlich habe nichts gegen eine manuelle Speicherverwaltung, wenn der Speicher an den Richtigen Stellen (Destructor der Liste bzw. bei mir TPlatine) UND beim löschen von Elementen freigegeben wird und gleichzeitig der/die Pointer auf das Element (nur in der Liste) vernichtet wird.
Naja, ich nehme an es gibt mehrere Objekte vom Typ TPlatine. (Wenn nicht, dann solltest du es sowieso frei implementieren).
Wenn du jetzt irgendwo ein Objekt TPlatine kopierst, dann wird der vector korrekt kopiert, aber die Pointer zeigen logischerweise auf den selben Storage. Wenn jetzt das erste TPlatine Objekt stirbt, ruft es für jedes Objekt im Storage delete auf, von daher wird allen Kopien der Boden unter den Füßen weggerissen.
-
Ethon schrieb:
Naja, ich nehme an es gibt mehrere Objekte vom Typ TPlatine. (Wenn nicht, dann solltest du es sowieso frei implementieren).
Wenn du jetzt irgendwo ein Objekt TPlatine kopierst, dann wird der vector korrekt kopiert, aber die Pointer zeigen logischerweise auf den selben Storage. Wenn jetzt das erste TPlatine Objekt stirbt, ruft es für jedes Objekt im Storage delete auf, von daher wird allen Kopien der Boden unter den Füßen weggerissen.
gut zu wissen, aber dieser Fall wird nicht eintreten, da es nur eine TPlatine gibt (Hauptansicht des Programms) und selbst wenn es mehrere TPlatine-Objekte gibt (MDI), wird nix kopiert, sondern einfach eine neue ("leere") Instanz erstellt.
-
So wäre eine möglicht elegante Implementierung mit unique_ptr, erfordert nen vernünftigen Compiler, also gcc 4.3 (oder 4.4 ?) oder neuer:
class TPlatine { public: typedef TElement value_type; private: typedef std::unique_ptr<value_type> value_ptr; std::vector<value_ptr> m_storage; public: template<typename... Args> void AddElement(Args const&... args) { m_storage.push_back(value_ptr(new value_type(args...))); } void DeleteElement(std::size_t index) { m_storage.erase(m_storage.begin() + index); } value_type* GetElement(std::size_t index) { if(index < m_storage.size()) return m_storage[index].get(); return nullptr; } };Kopieren kannst du damit auch nicht, der Compiler würde dir auf die Finger hauen.
da es nur eine TPlatine gibt (Hauptansicht des Programms)
Dann implementiere es doch frei, dann gehst du auf Nummer sicher.

-
Ethon schrieb:
So wäre eine möglicht elegante Implementierung mit unique_ptr, erfordert nen vernünftigen Compiler, also gcc 4.3 (oder 4.4 ?) oder neuer:
Danke schonmal, ich schaue den Code mal genau durch und versuche das mal so umzusetzen.
wozu ist diese Zeile da? bis vohin geht die template-definition? Wozu muss ich ein eigenes Template definieren?
template<typename... Args>sorry, wenn ich so blöd frage, aber mein Buch hat nur einen geringen Teil STL...ist halt ein Anfängerbuch

Hier nochmal der Aufruf mir ein gutes deutsches Buch zu nennen, wo die STL und die smart-Pointer mal richtig erklärt werden
Ethon schrieb:
Dann implementiere es doch frei, dann gehst du auf Nummer sicher.

mit freier Implementierung meinst du die Variante wie du sie oben gepostet hast? oder die mit der manuellen Speicherverwaltung?