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
Zitat
Smart pointers: No delete
Always use the standard smart pointers, and non-owning raw pointers. Never use owning raw pointers and delete, except in rare cases when implementing your own low-level data structure (and even then keep that well encapsulated inside a class boundary).
C-/C++-Quelltext |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
// [...] Rest siehe Testcase-Code struct Foo { int id; std::string name; Foo(int id, std::string const & name) : id{id} , name{name} { std::cout << "+" << this << "\n"; } virtual ~Foo() { std::cout << "-" << this << "\n"; } }; int main() { // create sole ownership auto owner = make_sole<Foo>(42, "Anonymous"); // create non-owning pointer dynamic_ptr<Foo> user{owner}; // operate on `user` user->id++; user->name += std::to_string(user->id); if (!user.expired()) { std::cout << "Foo[" << user->id << "," << user->name << "]\n"; } else { // this cannot happen in this case, because user didn't expire, yet std::cout << "Foo expired (unexpected!!)\n"; return 1; } // move ownership auto other = std::move(owner); std::cout << "After moving ownership, non-owning expired() is " << user.expired() << "\n"; // end ownership other = nullptr; std::cout << "After ownership ended, non-owning expired() is " << user.expired() << "\n"; } |
Eine unique ownership mit einer Benutzung von expired zu kombinieren ergibt keinen Sinn (Jedenfalls sehe ich keinen), da du dann ja shared_ptr verwenden sollst, da du das Objekt ja offensichtlich geteilt hast.
raw pointer also nur, wenn du weißt, dass das Objekt auf das du zeigst länger Lebt als du selbst
Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Databyte« (04.12.2014, 16:52)
C-/C++-Quelltext |
|
1 2 3 4 5 6 |
borrowed_ptr<int> b; { // scope for the owner owned_ptr<int> o = make_owned(0xDEADBEEF); b = o; } // owner dies, b holds a reference. int& x = *b; // throws |
C-/C++-Quelltext |
|
1 2 3 4 5 6 7 |
borrowed_ptr<int> b = o; do_some_stuff(); // Now use the borrowed pointer here. auto p = b.lock(); // Throws if owned object died. // Use p without failure here: *p += 42; *p %= 7; |
C-/C++-Quelltext |
|
1 2 3 4 5 6 7 |
owned_ptr<int> X = make_owned(42); auto t1 = std::thread( [](borrowed_ptr<int> x) {auto y = x.lock(); do_quite_a_lot_of_things_with(y);} , X ); auto t2 = std::thread([&X](){wait_a_bit(); X = nullptr;}); t1.join(); t2.join(); |
C-/C++-Quelltext |
|
1 2 3 4 |
int main(int _argc, char** _argv) noexcept { asm volatile("lock cmpxchg8b %eax"); return 0; } // ::main |
Zitat von »Glocke«
non-owning raw pointers als "weak_ptr-Ersatz" zu nehmen.
Zitat von »Glocke«
Damit bleibt allerdings der Vorteil von std::weak_ptr auf der Strecke: bool expired() const;
Warum es das nicht gibt, lässt sich einfach erklären: Um diese Funktionalität zu Implementieren ist einige Zusatzlogik mit Laufzeit Overhead und zusätzlichen potentiell sehr hohen Speicherverbrauch notwendig.
[...] entschloss sich am Ende einfach immer "std::shared_ptr" einzusetzen. Das ist eine einfache Lösung, ich bin aber nicht überzeugt das es eine Gute ist.
Deine Methode wird dort auch diskutiert. Der Nachteil bei dieser Variante ist natürlich der Overhead durch den "std::vector" und den Doppel-Pointer.
"insert", "replace" und "remove" würde außerdem nicht Teil der öffentlichen Schnittstelle machen.
Außerdem möchte ich darauf Hinweisen, dass es Probleme mit Exceptions geben könnte. Ein Destruktor darf keine Ausnahme werfen, "remove" kann das wegen "pop_back" aber theoretisch tun.
Außerdem solltest du unbedingt "virtual" vom Destruktor entfernen. Ich sehe keinen Sinn darin, außer das es einfach Ineffizient ist. Das Verdoppelt die Größe der Klasse im Prinzip sinnlos.
Multithreading ist in der Tat ein sehr großes Problem bei deiner Implementierung.
Das Thread Safe zu machen klingt für mich auf den ersten Blick nach einer sehr schweren Aufgabe bzw. einen Mutex.
C-/C++-Quelltext |
|
1 2 3 4 |
int main(int _argc, char** _argv) noexcept { asm volatile("lock cmpxchg8b %eax"); return 0; } // ::main |
Den vector halte ich für überflüssig. Es genügt, zu prüfen, ob ein borrowed_ptr noch gültig ist, oder eben nicht.
C-/C++-Quelltext |
|
1 2 3 4 5 6 7 8 9 10 11 12 |
template<typename T>struct ref_data { T* obj_ptr; std::atomic<size_t> borrowed; std::atomic<size_t> used; }; owned_ptr::owned_ptr(owned_ptr&& p) noexcept : obj_ptr_{nullptr}, ref_data_{nullptr} { std::swap(obj_ptr_, p.obj_ptr_ ); std::swap(ref_data_, p.ref_data_); } borrowed_ptr::borrowed_ptr(const owned_ptr& o) noexcept : ref_data_{o.ref_data_} {} |
C-/C++-Quelltext |
|
1 2 3 4 |
int main(int _argc, char** _argv) noexcept { asm volatile("lock cmpxchg8b %eax"); return 0; } // ::main |
Werbeanzeige