Kann intelligente Zeiger implizit als Zeiger verwendet werden?

stimmen
0

Sind intelligente Zeiger als Zeiger in Betracht gezogen? Und so können sie implizit als Zeiger verwendet?

Lassen Sie uns sagen, dass ich die folgende Klasse haben:

class MyClass {
    //...
    std::shared_ptr<AnotherClass> foo() { /*whatever*/ };
    void bar(AnotherClass* a) { /*whatever too*/ };
    //...
}

Dann kann ich MyClassdie folgende Art und Weise?

// m is an instance of MyClass
m.bar(m.foo());
Veröffentlicht am 09/10/2019 um 19:00
quelle vom benutzer
In anderen Sprachen...                            


3 antworten

stimmen
3

Nein , sie können nicht austauschbar verwendet werden. Sie würden einen Compiler - Fehler in Ihrem Beispiel bekommen. Aber man kann immer die Rohzeiger bekommen durch shared_ptr::get().

Beantwortet am 09/10/2019 um 19:02
quelle vom benutzer

stimmen
2

NEIN! Es wäre eine sein schrecklich API . Ja, man könnte es leicht innerhalb implementieren shared_ptr, aber nur , weil Sie bedeutet nicht , könnten Sie sollten.

Warum ist es so eine schlechte Idee? Der Klarzeigerbasierte Schnittstelle des barnicht eine Instanz des gemeinsam benutzten Zeiger beizubehalten. Wenn bargeschieht , irgendwo den rohen Zeiger zu speichern und dann verlassen, gibt es nichts , das garantiert , dass der Zeiger gespeichert war nicht in der Zukunft baumelt werden. Der einzige Weg , dies zu garantieren , wäre eine Instanz des gemeinsamen Zeigers zu behalten, nicht die rohen Zeiger (das ist der springende Punkt shared_ptr!).

Es kommt noch schlimmer: der folgende Code nicht definiertes Verhalten , wenn ist foo()gibt eine Zeiger - Instanz , die nur eine Referenz hatte , als foo()zurück (zB wenn fooeine einfache Fabrik neuer Objekte ist):

AnotherClass *ptr = m.foo().get();
// The shared_ptr instance returned by foo() is destroyed at this point
m.bar(ptr); // undefined behavior: ptr is likely a dangling pointer here

Hier sind die Optionen; diejenigen, die zuvor zunächst prüfen, bevor ihre Nachfolger unter Berücksichtigung.

  • Wenn bar(AnotherClass *)eine externe API ist, dann müssen Sie es auf sichere Weise wickeln, dh den Code, genannt hätte Original::barsollten anrufen werden MyWrapped::bar, und die Umhüllung sollte , was Lebensdauerverwaltung tun notwendig ist. Angenommen, es ist startUsing(AnotherClass *)und finishUsing(AnotherClass *)und der Code erwartet , dass der Zeiger bleibt gültig zwischen startUsingund finishUsing. Ihr Wrapper wäre:

    class WithUsing {
      std::unique_ptr<AnotherClass> owner; /* or shared_ptr if the ownership is shared */
      std::shared_ptr<User> user;
    public:
      WithUsing(std::unique_ptr<AnotherClass> owner, std::Shared_ptr<User> user) :
        owner(std::move(owner)), user(std::move(user)) {
        user.startUsing(owner.get());
      }
      void bar() const {
        user.bar(owner.get());
      }
      ~WithUsing() {
        user.finishUsing(owner.get());
      }
    };
    

    Sie würden dann verwenden WithUsingals Griff zum UserObjekt und ggf. gezogene Nutzungen durch diesen Griff getan werden würden, die Existenz des Objekts zu gewährleisten.

  • Wenn AnotherClasskopierbar ist und ist sehr billig zu kopieren (zB es einen Zeiger oder zwei besteht), dann ist es nach Wert übergeben:

    void bar(AnotherClass)
    
  • Wenn die Umsetzung barnicht braucht den Wert zu ändern, kann es sein definiert einen const-Wert zu nehmen (die Erklärung ohne die sein kann , constwie es spielt keine Rolle , dort):

    void bar(const AnotherClass a) { ... }
    
  • Wenn barkein Zeiger speichert, dann tun Sie es nicht einen Zeiger übergeben: eine konstante Referenz standardmäßig übergeben, oder eine nicht konstante Referenz , wenn nötig.

    void bar(const AnotherClass &a);
    void bar_modifies(AnotherClass &a);
    
  • Wenn es sinnvoll ist , berufen barmit „kein Objekt“ (auch bekannt als „null“), dann gilt :

    1. Wenn Weitergabe AnotherClassvon Wert in Ordnung ist, dann verwenden std::optional:

      void bar(std::optional<AnotherClass> a);
      
    2. Andernfalls, wenn das AnotherClassEigentum nimmt, Gang unique_ptrfunktioniert gut , da es null sein kann.

    3. Ansonsten Gang shared_ptrfunktioniert gut , da es null sein kann.

  • Wenn foo()ein neues Objekt erstellt (gegen ein Objekt zurückkehrt , die bereits vorhanden ist ), sollte es zurückkehrt unique_ptrsowieso nicht ein shared_ptr. Werksfunktionen sollten eindeutige Hinweise zurückkehren: das ist idiomatische C ++. Dadurch ist sonst verwirrend, da eine Rückkehr shared_ptr gemeint bestehende gemeinsame Verantwortung zum Ausdruck bringen .

    std::unique_ptr<AnotherClass> foo();
    
  • Wenn barEigentum an dem Wert annehmen soll, dann sollte es einen eindeutigen Zeiger zu akzeptieren sein - das ist das Idiom für „Ich nehme über die Lebensdauer des Objekts Verwaltung“:

    void bar(std::unique_ptr<const AnotherClass> a);
    void bar_modifies(std::unique_ptr<AnotherClass> a);
    
  • Wenn barsollte gemeinsame Verantwortung behalten, dann sollte es nehmen sein shared_ptr, und Sie werden sofort die Umwandlung der unique_ptrvon zurück foo()zu einem gemeinsamen eins:

    struct MyClass {
      std::unique_ptr<AnotherClass> foo();
      void bar(std::shared_ptr<const AnotherClass> a);
      void bar_modifies(std::shared_ptr<AnotherClass> a);
    };
    
    void test() {
      MyClass m;
      std::shared_ptr<AnotherClass> p{foo()};
      m.bar(p);
    }
    

shared_ptr(const Type)und shared_ptr(Type)das Eigentum teilen, sie sorgen für eine konstante Ansicht und eine änderbare Ansicht des Objekts ist. shared_ptr<Foo>auch umwandelbar ist shared_ptr<const Foo>(aber nicht umgekehrt, Sie verwenden würden const_pointer_castdafür (mit Vorsicht). Sie sollten immer Objekte als Konstanten für den Zugriff, Ausfall- und nur mit nicht konstanten Typen zu arbeiten , wenn es dafür eine explizite Notwendigkeit ist.

Wenn eine Methode etwas nicht ändern, machen es sich von selbst , dass Dokument Tatsache , indem er einen Verweis / Zeiger akzeptieren , const somethingstatt.

Beantwortet am 09/10/2019 um 19:10
quelle vom benutzer

stimmen
2

Intelligente Zeiger werden verwendet , um sicherzustellen , dass ein Objekt gelöscht wird , wenn es nicht mehr verwendet wird (referenziert).

Smart-Pointer gibt es Lebensdauer des Zeigers verwalten sie besitzen / Aktie.

Sie können von einem Wrapper denken , die einen Zeiger nach innen hat. So ist die Antwort nein. Sie können jedoch auf den Zeiger zugreifen , die sie über eigene get()Methode.

Bitte beachten Sie, dass es nicht so schwierig ist , baumelt Zeiger zu machen , wenn Sie verwenden getMethode, also wenn Sie es besonders vorsichtig sein verwenden.

Beantwortet am 09/10/2019 um 19:08
quelle vom benutzer

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more