Μπορεί έξυπνη δείκτες να χρησιμοποιηθεί έμμεσα ως δείκτες;

ψήφοι
0

Οι έξυπνες δείκτες θεωρούνται ως δείκτες; Και ως εκ τούτου μπορούν να χρησιμοποιηθούν εμμέσως ως δείκτες;

Ας πούμε ότι έχω την εξής κατηγορία:

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

Τότε μπορώ να χρησιμοποιήσω MyClassτον ακόλουθο τρόπο;

// m is an instance of MyClass
m.bar(m.foo());
Δημοσιεύθηκε 09/10/2019 στις 19:00
πηγή χρήστη
Σε άλλες γλώσσες...                            


3 απαντήσεις

ψήφοι
3

Όχι ότι δεν μπορούν να χρησιμοποιηθούν εναλλάξιμα. Θα λάβετε ένα σφάλμα compiler στο παράδειγμά σας. Αλλά μπορείτε πάντα να πάρετε την πρώτη δείκτη του shared_ptr::get().

Απαντήθηκε 09/10/2019 στις 19:02
πηγή χρήστη

ψήφοι
2

ΟΧΙ! Θα ήταν μια φοβερή API . Ναι, θα μπορούσε εύκολα να εφαρμόσει μέσα shared_ptr, αλλά μόνο και μόνο επειδή θα μπορούσε να δεν σημαίνει ότι θα έπρεπε.

Γιατί είναι τόσο κακή ιδέα; Η πεδιάδα-δείκτη-based interface του barδεν διατηρεί μια παρουσία του κοινόχρηστου δείκτη. Αν barσυμβαίνει να αποθηκεύσει τις πρώτες δείκτη κάπου και, στη συνέχεια, βγείτε, δεν υπάρχει τίποτα που να εγγυάται ότι ο δείκτης είχε αποθηκευτεί δεν θα γίνει κρέμονται στο μέλλον. Ο μόνος τρόπος για να εξασφαλιστεί ότι θα είναι να διατηρήσει μια εμφάνιση του κοινού δείκτη, δεν είναι η πρώτη δείκτης (αυτό είναι το νόημα της shared_ptr!).

Η κατάσταση χειροτερεύει: ο παρακάτω κώδικας είναι απροσδιόριστη συμπεριφορά, εάν foo()επιστρέψει ένα παράδειγμα δείκτη που είχε μόνο μία αναφορά όταν foo()επέστρεψε (π.χ. αν fooείναι ένα απλό εργοστάσιο των νέων αντικειμένων):

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

Εδώ είναι οι επιλογές? θεωρούν αυτά που αναφέρονται παραπάνω πρώτη πριν από την εξέταση των διαδόχων τους.

  • Αν bar(AnotherClass *)είναι μια εξωτερική API, τότε θα πρέπει να το τυλίξετε με ασφαλή τρόπο, δηλαδή τον κώδικα που θα ονομάζεται Original::barθα πρέπει να ζητούν MyWrapped::barκαι το περιτύλιγμα πρέπει να κάνει ό, τι της διαχείρισης της ζωής είναι απαραίτητη. Ας υποθέσουμε ότι υπάρχει startUsing(AnotherClass *)και finishUsing(AnotherClass *)και ο κωδικός αναμένει ο δείκτης να παραμείνει σε ισχύ μεταξύ των startUsingκαι finishUsing. Περιτύλιγμα σας θα είναι:

    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());
      }
    };
    

    Στη συνέχεια, θα χρησιμοποιήσει WithUsingως λαβή για να το Userαντικείμενο, καθώς και κάθε χρήση θα μπορούσε να γίνει μέσω αυτής της λαβής, εξασφαλίζοντας την ύπαρξη του αντικειμένου.

  • Αν AnotherClassείναι copyable και είναι πολύ φθηνή για να αντιγράψετε (π.χ. αποτελείται από ένα δείκτη ή δύο), τότε θα περάσει από τιμή:

    void bar(AnotherClass)
    
  • Εάν η εφαρμογή της barδεν χρειάζεται να αλλάξετε την τιμή, μπορεί να οριστεί να λάβει const τιμή (η δήλωση μπορεί να είναι χωρίς το constόπως δεν έχει σημασία εκεί):

    void bar(const AnotherClass a) { ... }
    
  • Αν barδεν αποθηκεύει ένα δείκτη, τότε μην το περάσει ένα δείκτη: περάσει μια const αναφορά από προεπιλογή, ή μη-const αναφοράς, εάν είναι απαραίτητο.

    void bar(const AnotherClass &a);
    void bar_modifies(AnotherClass &a);
    
  • Αν είναι λογικό να επικαλεστεί barμε «χωρίς αντικείμενο» (γνωστός και ως «null»), τότε:

    1. Αν πέρασμα AnotherClassαπό την τιμή είναι εντάξει, τότε χρησιμοποιήστε std::optional:

      void bar(std::optional<AnotherClass> a);
      
    2. Σε αντίθετη περίπτωση, εάν AnotherClassπαίρνει την ιδιοκτησία, περνώντας unique_ptrδουλεύει μια χαρά, δεδομένου ότι μπορεί να είναι null.

    3. Σε αντίθετη περίπτωση, περνώντας shared_ptrδουλεύει μια χαρά, δεδομένου ότι μπορεί να είναι null.

  • Αν foo()δημιουργεί ένα νέο αντικείμενο (σε σχέση με την επιστροφή του ένα αντικείμενο που ήδη υπάρχει), θα πρέπει να επιστρέψει unique_ptrούτως ή άλλως, δεν είναι ένα shared_ptr. Λειτουργίες εργοστάσιο πρέπει να επιστρέψει μοναδική δείκτες: αυτό είναι ιδιωματικές ++ Γ. Κάτι άλλο είναι συγκεχυμένη, καθώς επιστρέφοντας ένα shared_ptr έχει ως στόχο να εκφράσει τις υπάρχουσες κοινής ιδιοκτησίας .

    std::unique_ptr<AnotherClass> foo();
    
  • Εάν barθα πρέπει να αναλάβετε την κατοχή της αξίας, τότε θα πρέπει να δέχεται ένα μοναδικό δείκτη - αυτό είναι το ιδίωμα για «Παίρνω πάνω από τη διαχείριση της ζωής του αντικειμένου»:

    void bar(std::unique_ptr<const AnotherClass> a);
    void bar_modifies(std::unique_ptr<AnotherClass> a);
    
  • Αν barπρέπει να διατηρήσουν συνιδιοκτησίας, τότε θα πρέπει να λαμβάνει shared_ptr, και θα σας αμέσως μετατροπή του unique_ptrεπέστρεψε από foo()ένα κοινόχρηστο ένα:

    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)και shared_ptr(Type)θα μοιράζονται την ιδιοκτησία, που παρέχουν μια σταθερή άποψη και τροποποιήσιμο άποψη του αντικειμένου, αντίστοιχα. shared_ptr<Foo>Επίσης μετατρέψιμες σε shared_ptr<const Foo>(αλλά όχι το αντίθετο, θα χρησιμοποιούσατε const_pointer_castγια να (με προσοχή). Θα πρέπει πάντα να προκαθορίσει την πρόσβαση σε αντικείμενα όπως σταθερές και μόνο σε συνεργασία με μη σταθερή τύπων, όταν υπάρχει μια σαφής ανάγκη για αυτό.

Αν μια μέθοδος δεν τροποποιεί κάτι, το κάνει αυτο-έγγραφο το γεγονός αυτό, έχοντας αποδεχθεί μια αναφορά / δείκτη σε const somethingαντ 'αυτού.

Απαντήθηκε 09/10/2019 στις 19:10
πηγή χρήστη

ψήφοι
2

Smart δείκτες χρησιμοποιούνται για να βεβαιωθείτε ότι ένα αντικείμενο έχει διαγραφεί εάν δεν χρησιμοποιείται πλέον (αναφορά).

Έξυπνες δείκτης είναι εκεί για να διαχειριστεί τη διάρκεια της ζωής του δείκτη που κατέχουν / μετοχή.

Μπορείτε να σκεφτείτε ένα περιτύλιγμα που έχει ένα δείκτη στο εσωτερικό του. Έτσι, η απάντηση είναι όχι. Ωστόσο, μπορείτε να έχετε πρόσβαση στο δείκτη που κατέχουν μέσω της get()μεθόδου.

Παρακαλώ σημειώστε ότι δεν είναι τόσο δύσκολο να κουνάμε δείκτες, αν χρησιμοποιείτε getτη μέθοδο, οπότε αν το χρησιμοποιήσετε να είναι ιδιαίτερα προσεκτικοί.

Απαντήθηκε 09/10/2019 στις 19:08
πηγή χρήστη

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