Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

buggypixels

Treue Seele

Beiträge: 125

Wohnort: Meerbusch

Beruf: Programmierer

  • Private Nachricht senden

11

05.08.2014, 08:47

All diese Smart/Unique und was weiß ich Pointer Sachen haben sich mir nie erschlossen.
Alles unnötige Hilfskonstrukte meiner Meinung nach.
Also die erste Frage ist halt, wer ist für den Lebenszyklus (Owner/Ownership) eines Book zuständig. Das erkennt man leider
in deinem Beispiel nicht. Wer erstellt eine Instanz von Book? Der sollte dann auch für das Löschen zuständig sein.
Wie BlueCobold richtig sagt ist die Verwendung eines RawPointers vollkommen korrekt dann.
Die Frage ist halt wie immer bei einer "get" Methode, wie man deutlich macht, dass eventuell kein Book da ist.
Hier gibt es mehrere Möglichkeiten

C-/C++-Quelltext

1
2
3
4
5
6
7
map<uint32_t,Book> m_Books;
Book* getBook(uint32_t id) {
  if ( ... found ... ) {
    return &m_Books[id];
  }
  return 0;
}

Dann kann der Aufrufer auf "0" testen und weiß wann kein Buch da ist.
Ein anderer Ansatz ist, dass Du einen Pointer reinreichst und dann noch einen bool als Rückgabewert hast:

C-/C++-Quelltext

1
2
3
4
5
6
7
bool getBook(uint32_t id,Book* book) {
  if ( .. found... ) {
     book->id = m_Books[id].id;
    return true;
  }
  return false;
}

Den Ansazt verwendet Microsoft z.B. in DirectX wo allerdings ja immer HRESULT der Rückgabewert ist.
Der dritte Ansatz wäre mit Referenzen zu arbeiten und dann mittels assert sicherzustellen, dass ein Book existiert:

C-/C++-Quelltext

1
2
3
4
const Book& getBook(uint32_t id) {
  assert(... book exists .... );
  return m_Books[id];
}

Der Vorteil ist, dass Du nicht mit Pointer arbeitest und niemand Dir Dein Book löschen kann. Der Nachteil ist, dass Du
nur einen Check zur Laufzeit hast und das assert dein Programm beendet. Alternative wäre sicherlich noch eine

C-/C++-Quelltext

1
2
3
4
5
6
bool contains(uint32_t id) {
  if ( ... book exists ... ) {
    return true;
  }
  return false;
}

Methode. Dann kannst Du das vorher abfragen und bleibst bei den Referenzen. Übrigens ist es immer sinnvoll beide
Arten dann zu implementieren, sprich eine const und eine ohne const

C-/C++-Quelltext

1
2
const Book& get(uint32_t id) const {}
Book& get(uint32_t id) {}

Wie Du es machst es auch persönlicher Geschmack. Hauptsachen man macht es dann konsistent innerhalb eines Projektes.
Außerdem siehst Du an den Beispielen, dass diese Smart Pointer gar nicht nötig sein und durch ein bisschen überlegen über Onwership etc. auch
völlig unnötig.

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

12

05.08.2014, 09:42

Unnötig ist vieles in Programmiersprachen. Aber gerade diese unnötigen Sachen nehmen einem oft sehr viel Arbeit ab. Lambda-Expressions sind letztlich ja auch unnötig und "++" ebenfalls. Letztlich ist sogar "for" und "while do" unnötig, das alles kann man nämlich auch als "do while" schreiben. Wahlweise andersrum. Sogar alle 3 sind durch "goto" völlig unnötig. Das heißt aber nicht, dass man diese Dinge deswegen nicht trotzdem nutzen sollte. Im Gegenteil, Smart-Pointer sparen einem sehr viel Arbeit und sehr viel Ärger. Ich möchte nicht erst in jedem Destruktor meine Liste von Pointern durchgehen und alle manuell löschen müssen. Vector<Unique> und fertig ist der Lack.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

13

05.08.2014, 14:40

Mhm, den Sinn der Smart-Pointer sehe ich im Moment auch nur darin, dass man sich Anweisungen bei der Destruktion spart und ggf nicht freigegebener Speicherbereich definitiv wieder freigegeben wird.
Dass ich dadurch Memory Access Violations nicht verhindern kann, wenn ich auf schon deallokierten Speicherbereich zugreife, ist schade, wobei ich eigentlich dachte, dass Reference Counting genau dafür da ist (shared_ptr) ^^

Nunja, ich verwende jetzt Raw Pointer für die Rückgabe, wie auch sonst immer ;)
EnvisionGame(); EnableGame(); AchieveGame(); - Visionen kann man viele haben. Sie umzusetzen und auf das Ergebnis stolz zu sein ist die eigentliche Kunst.

Legend

Alter Hase

Beiträge: 731

Beruf: Softwareentwickler

  • Private Nachricht senden

14

05.08.2014, 14:57

Ein Vorteil von Smartpointern der zu Fuß sehr schwer umzusetzen ist: Sicherzustellen, dass Ressourcen auch bei Exceptions freigegeben werden.

Ein Beispiel wo das noch ganz gut geht:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
void Funktion ()
{
   Klasse* obj = new Klasse ();

   if (Bedingung)
   {
      delete obj;
      throw Exception ("warum auch immer");
   }

   delete obj;
}


Bauen wir das aber mal aus:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void Funktion ()
{
   Klasse* obj = new Klasse ();

   WasIstWennIchEineExceptionSchmeisse();

   if (Bedingung)
   {
      delete obj;
      throw Exception ("warum auch immer");
   }

   delete obj;
}


Wenn WasIstWennIchEineExceptionSchmeisse eine Exception wirklich schmeißt, dann habe ich hier ein Speicherleck. Mit Smartpointern hilft mir da der Compiler.
Und je nach dem wie komplex Aufrufhierarchien werden glaube ich nicht, dass man das zu Fuß wirklich verhindern kann.
"Wir müssen uns auf unsere Kernkompetenzen konzentrieren!" - "Juhu, wir machen eine Farm auf!"

Netzwerkbibliothek von mir, C#, LGPL: https://sourceforge.net/projects/statetransmitt/

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

15

05.08.2014, 15:05

Dass ich dadurch Memory Access Violations nicht verhindern kann, wenn ich auf schon deallokierten Speicherbereich zugreife, ist schade, wobei ich eigentlich dachte, dass Reference Counting genau dafür da ist (shared_ptr)
Reference Counting ist unter anderem natürlich für sowas da. Aber es ist nicht so, dass C++ durch Smart-Pointer auf komplette Garbage-Collection umgestellt wurde. Das kann man mit shared_ptr technisch natürlich sogar machen, ist aber nicht die Intention der Smart-Pointer in C++.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

16

06.08.2014, 09:39

Ich mag Smartpointer, weil sie nicht nur nützliche Hilfskonstrukte sind, sondern auch eine neue Bedeutungsebene einführen.
Wenn ich ein Objekt besitze, habe ich einen unique-Pointer, wenn ich nur eine Referenz (im abstrakten Sinne) darauf brauche, habe ich einen raw-Pointer. Dank der schönen move-Semantik in C++11 ist dann immer sofort klar, wer ein Objekt besitzt und wer es demnach freigeben wird und Memory-Leaks werden nahezu unmöglich (mir fällt ehrlich gesagt nicht ein, wie man eines verursachen könnte, wenn man es nicht mutwillig darauf anlegt...)

Und dadurch hat man nicht nur als Programmierer eine bessere Übersicht. Als ich neulich mein Speichersystem geschrieben habe, konnte ich wunderbar Funktionen überladen, um Objekte zu laden oder zu speichern. Ist der Typ ein unique-Pointer wird das Objekt tatsächlich in die Datei geschrieben, ist es ein raw-Pointer braucht man nur eine ID oder ähnliches zu schreiben. Beim Laden wird dementsprechend entweder ein neues Objekt erstellt, oder es wird sich ein Zeiger auf ein bereits geladenes besorgt - und das geht vollautomatisch.
Lieber dumm fragen, als dumm bleiben!

Werbeanzeige