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

1

04.08.2014, 16:00

Verständnis smart pointers

Heyho,

ich arbeite noch nicht lange mit den smart pointern in C++. Da ich aber an einem relativ neuen Projekt arbeite, wöllte ich das doch gern von Anfang an richtig machen :)

Ich möchte aus einer map eine Instanz laden, die einem Controller übergeben, welcher die Instanz dann weiter an die View leitet.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
struct SBook {...};
class Controller {...};
class View {...};
class Data {
private:
    map<unsigned int, SBook> m_Books;
    shared_ptr<SBook> Data::GetBook(unsigned int id) {
        ...
        return make_shared<SBook>(foundBookIt->second);
    }
};


Frage: Welche der folgenden Methoden ist "richtig"? Oder kenne ich sogar eine noch garnicht oO
Was ist mit "ownership" gemeint? Der Ownership über die Instanz oder den pointer?

Methode 1:

C-/C++-Quelltext

1
2
3
4
5
6
7
void Controller::DoSomething() {
    shared_ptr<SBook> pBook = m_pData->GetBook(anyID);
    m_pView->DoSomething(pBook);
}
void View::DoSomething(shared_ptr<SBook> pBook) { // as per-val shared ptr
    pBook->Burn();
}

Methode 2:

C-/C++-Quelltext

1
2
3
void View::DoSomething(const shared_ptr<SBook>& pBook) { // as const ref shared ptr!
   pBook->Burn();
}

Methode 3:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
void Controller::DoSomething() {
    shared_ptr<SBook> pBook = m_pData->GetBook(anyID);

    m_pView->DoSomething(pBook); // implicit cast to weak_ptr?
}
void View::DoSomething(weak_ptr<SBook> pBook) { // as weak ptr per val
   auto pOwnedBook = pBook.lock();
   pOwnedBook->Burn();
}
EnvisionGame(); EnableGame(); AchieveGame(); - Visionen kann man viele haben. Sie umzusetzen und auf das Ergebnis stolz zu sein ist die eigentliche Kunst.

Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

2

04.08.2014, 16:17

In der Regel empfehle ich in 99% der Fälle die Verwendung von "std::unique_ptr", der wesentlich einfacher nachzuvollziehen und verwenden ist und keinen Overhead hat.

Ownership ist, wie jeder Übersetzer dir sagen sollte, einfach der Besitz. Das Buch ist im Besitz von einem anderen Objekt oder beim "std::shared_ptr" in der Regel mehreren. Wenn alle Besitzer(Owners) zerstört sind wird der Besitz(Ownership) also das Buch zerstört.

Richtig sind alle Methoden die du geschrieben hast. In der Regel(so wie es auch hier aussieht) würde ich allerdings Raw-Pointer übergeben und keinen Smart-Pointer. Dann kann auch nicht der der Owner nach außen getragen werden. Raw-Pointer sind nach wie vor die beste Wahl, wenn der Owner nicht wechselt.

NachoMan

Community-Fossil

Beiträge: 3 885

Wohnort: Berlin

Beruf: (Nachhilfe)Lehrer (Mathematik, C++, Java, C#)

  • Private Nachricht senden

3

04.08.2014, 16:20

Versuchs mal mit einer:

C-/C++-Quelltext

1
map<unsigned int, std::unique_ptr<SBook> > m_Books;

Zurückgegeben wird dann:

C-/C++-Quelltext

1
2
const SBook& Data::GetBook(unsigned int id) const
SBook& Data::GetBook(unsigned int id)

Wie sonst eigentlich auch...
"Der erste Trunk aus dem Becher der Erkenntnis macht einem zum Atheist, doch auf dem Grund des Bechers wartet Gott." - Werner Heisenberg
Biete Privatunterricht in Berlin und Online.
Kommt jemand mit Nach oMan?

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

4

04.08.2014, 16:32

Statt dem SBook& bietet sich allerdings auch ein SBook* an damit da nullptr zurückkommen kann, falls der gesuchte Eintrag nicht existiert. Wie meine beiden Vorgänger schon sagten ist ein Smart-Pointer an der Stelle eher die falsche Wahl.
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]

5

04.08.2014, 16:42

Ich musste auch erst auf die harte Tour erfahren, dass Smartpointer nicht das Mittel für alles ist.
So lange du den Besitz, und dementsprechend die Verantwortung zum Löschen, an ein anderes Objekt übergeben möchtest, solltest du nur (wie meine Vorredner schon sagten) einen raw pointer zurück geben.

Nimelrian

Alter Hase

Beiträge: 1 216

Beruf: Softwareentwickler (aktuell Web/Node); Freiberuflicher Google Proxy

  • Private Nachricht senden

6

04.08.2014, 16:43

Wie meine beiden Vorgänger schon sagten ist ein Smart-Pointer an der Stelle eher die falsche Wahl.


Smart oder Shared? :)
Ich bin kein UserSideGoogleProxy. Und nein, dieses Forum ist kein UserSideGoogleProxyAbstractFactorySingleton.

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

7

04.08.2014, 16:52

Smart jeder Art.
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]

8

04.08.2014, 17:01

Alles klar, danke erstmal :)

Zitat

Ownership ist, wie jeder Übersetzer dir sagen sollte, einfach der Besitz. Das Buch ist im Besitz von einem anderen Objekt oder beim "std::shared_ptr" in der Regel mehreren.
Was Owner heißt weiß ich schon :D Die Frage war nur, von WAS der Owner - deiner Antwort entehme ich, der Owner von der Instanz ;)

Zitat

Wenn alle Besitzer(Owners) zerstört sind wird der Besitz(Ownership) also das Buch zerstört.
Naja es geht mir ja gerade darum, zu vermeiden, dass ein Book schon aus der Map entfernt wird, solange es noch außerhalb benutzt wird und möglicherweise invalid wird ;)
Also könnte man ja möglicherweise auch folgendes machen?:

C-/C++-Quelltext

1
2
map<unsigned int, shared_ptr<SBook>> books;
shared_ptr<SBook> GetBook(unsigned int);
... dann könnte man in der Delete Funktion von Data ja auch mit unique() ein vorzeitiges löschen verhindern und ggf eine Assertion ausgeben, um leaks aufzudecken:

C-/C++-Quelltext

1
2
3
4
5
void Data::Delete(unsigned int id) {
    ...
    assert(foundBookIt->second.unique());
    m_Books.erase(id);
}
... oder wäre das dann auch nicht gut?

Zitat

So lange du den Besitz, und dementsprechend die Verantwortung zum Löschen, an ein anderes Objekt übergeben möchtest, solltest du nur (wie meine Vorredner schon sagten) einen raw pointer zurück geben.
Hast du da ein "nicht" vergessen?
Ansonsten möchte ich ja vermeiden, dass der Pointer, den GetBook() zurückgibt, nicht dazu benutzt wird, das Objekt zu löschen ;) (delete m_pData->GetBook());
EnvisionGame(); EnableGame(); AchieveGame(); - Visionen kann man viele haben. Sie umzusetzen und auf das Ergebnis stolz zu sein ist die eigentliche Kunst.

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

9

04.08.2014, 17:45

Hast du da ein "nicht" vergessen?
Nein, hat er nicht. Ein Shared-Ptr wäre Übertragung der Ownership. Und genau das wäre falsch. Ein Raw-Pointer wäre korrekt. Um Gültigkeit musst Du Dir dabei anderweitig Gedanken machen. Natürlich macht es keinen Sinn, dass ein Buch außerhalb noch verwendet wird, obwohl die eigentliche Instanz schon vom Owner gelöscht wurde. Aber wenn ich Dir meinen Wohnungsschlüssel wegnehme, darfst Du auch nicht mehr in meine Wohnung ;) Oft ist das aber kein Problem, weil Instanzen hierarchisch verschachtelt sind und eine untere Schicht nur Raw-Pointer aus einer höheren Schicht bekommt. Daher ist das untere Objekt im Normalfall schon weg, bevor das Pointer-Objekt gelöscht wird.

Wenn das Objekt extern gelöscht wird, ist das genauso ein Fehler wie eine absichtliche Verwendung eines nullptr. Sollte Dich nicht interessieren.
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]

Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

10

04.08.2014, 17:51

Zitat von »iSmokiieZz«

dass ein Book schon aus der Map entfernt wird, solange es noch außerhalb benutzt wird

Das ist der Denk- bzw. Designfehler. Du solltest dafür sorgen, dass die Map(als Owner) erst zerstört wird wenn die untergeorndeten Objekte nicht mehr verwendet werden. Ein "std::shared_ptr" ist hier wieder so etwas wie die Brecheisenmethode es doch irgendwie hinzubekommen, dass die Lebenszeit der Untergeordneten Objekte länger ist als die des übergeordneten Objektes. Das ist aber prinzipiell nicht gut.

Zitat von »iSmokiieZz«

... dann könnte man in der Delete Funktion von Data ja auch mit unique() ein vorzeitiges löschen verhindern und ggf eine Assertion ausgeben, um leaks aufzudecken:

Wenn man gleich "std::unique_ptr" verwendet ist das unnötig. Da gibt es nur einen Besitzer und das ist die Map. Damit ist Lebensdauer der Bücher automatisch mit Lebensdauer des Controllers definiert und weiteres Prüfen nicht möglich bzw. einfach unnötig.

In dem Fall solltest du dir die Smart-Pointer sogar komplett sparen können und die Objekte ohne jeden Pointer in der Map speichern. Einfacher geht es nicht. Lebensdauer ist auch wieder die selbe wie bei "std::unique_ptr". Eine Map invalidiert auch nicht die Pointer.

Zitat von »iSmokiieZz«

Ansonsten möchte ich ja vermeiden, dass der Pointer, den GetBook() zurückgibt, nicht dazu benutzt wird, das Objekt zu löschen ;)

Das funktioniert so nicht. Man kann ja jederzeit wieder aus dem "std::shared_ptr" wieder einen normalen Zeiger rausziehen und entsprechend löschen versuchen. Und das egal ob shared, unique oder weak. Man kann auch jederzeit "std::exit(0)" machen oder was auch immer für Quatsch. Dagegen ist einfach kein Kraut gewachsen. Smart-Pointers schützen nicht vor Dummheit, sie schützen nur gegen Speicherlecks und geben die Besitzerbeziehungen an.
Dieses Ziel wirst du so nicht erreichen können.

Werbeanzeige