2015-11-14 15 views
9

E 'questo il modo corretto di utilizzare puntatori std intelligenti per garantire la sicurezza ptrmodo corretto di utilizzare puntatori std intelligenti per garantire la sicurezza ptr

Questo esempio non può essere la migliore, ma sto cercando di emulare il codice vero e proprio. Il problema che avevo era nel codice reale, il puntatore del comunicatore era un puntatore non elaborato che poteva essere deselezionato in qualsiasi momento, causando un arresto anomalo nell'utilizzo del puntatore.

Così ho deciso di esaminare std :: shared_ptr e std :: weak_ptr per vedere come dovrebbe essere progettato ora abbiamo C++ 11. Io uso un weak_ptr nel codice di invio che controlla se il ptr è ancora valido e solo allora verrà dereferenziato il ptr. Questo codice è l'approccio corretto? Qualche miglioramento?

#include <memory> 
#include <iostream> 
#include <string> 

class communicator 
{ 
public: 
    communicator(const char* name, int comport, int speed) : name_(name), comport_(comport), speed_(speed) { } 

    void send(const std::string& s) { 
     std::cout << "sending " << s << " using " << name_ << " at " << speed_ << " rate and using com port " << comport_ << '\n'; 
    } 

private: 
    const char* name_; 
    int comport_; 
    int speed_; 
}; 

class sender 
{ 
public: 
    sender() {} 

    void set_communicator(std::weak_ptr<communicator> comms) { 
     comms_ = comms; 
    } 

    void send(const std::string& s) 
    { 
     if (auto sh = comms_.lock()) 
      sh->send(s); 
     else 
      std::cout << "Attempting to send: " << s << " but ptr no longer exists\n"; 
    } 

private: 
    std::weak_ptr<communicator> comms_; 
}; 

int main() { 

    sender mysender; 

    { 
     // create comms object 
     std::shared_ptr<communicator> comms(new communicator("myname", 3, 9600)); 

     mysender.set_communicator(comms); 

     mysender.send("Hi guys!"); 

    } // comms object gets deleted here 

    mysender.send("Hi guys after ptr delete!"); 
} 

uscita:

sending Hi guys! using myname at 9600 rate and using com port 3 
Attempting to send: Hi guys after ptr delete! but ptr no longer exists 
+1

Potrebbe essere più appropriato per codereview. – Jarod42

+5

Vorrei usare 'std :: make_shared'. – Jarod42

risposta

4

puntatore che potrebbe essere de-allocata in qualsiasi momento - con conseguente in un incidente nel usando il puntatore

Questo è il sintomo dietro la logica per l'introduzione di un weak_ptr; quindi considererei il tuo approccio basato su weak_ptr a destra.

Ciò che trovo discutibile, tuttavia, è che in concomitanza con questo

sender() : comms_() {} 

void set_communicator(std::weak_ptr<communicator> comms) { 
    comms_ = comms; 
} 

sorta-di due fasi di costruzione di attività interna s' il sendercomms_ non si reimposta lo stato della risorsa interna a after-construction-state una volta lock() fallisce in

void send(const std::string& s) 

Ma questo non è "sbagliato" da solo; è solo qualcosa che potrebbe essere considerato per l'app in scala.

L'altra cosa è che non si throw (o let throw by the shared_ptr(weak_ptr) ctor (#11)) quando lock() fallisce, ma solo if-else gestire questo. Non posso conoscere i requisiti della tua app a schermo intero, ma basandomi sull'estratto che hai assemblato, la gestione degli errori basata su eccezioni avrebbe migliorato il design.

Esempio:

#include <memory> 
#include <stdexcept> 
#include <iostream> 
#include <string> 

class communicator 
{ 
public: 
    communicator(const char* name, int comport, int speed) 
     : name_(name), comport_(comport), speed_(speed) { } 

    void send(const std::string& s) { 
     std::cout << "sending " << s << " using " << name_ << " at " 
        << speed_ << " rate and using com port " << comport_ 
        << '\n'; 
    } 

private: 
    const char* name_; 
    int comport_; 
    int speed_; 
}; 

class sender 
{ 
public: 
    struct invalid_communicator : public std::runtime_error { 
     invalid_communicator(const std::string& s) : 
      std::runtime_error(
       std::string("Attempting to send: \"") + s 
        + "\" but communicator is invalid or not set" 
      ) {} 
    }; 

    sender() : comms_() {} 

    void set_communicator(std::weak_ptr<communicator> comms) { 
     comms_ = comms; 
    } 

    /* non-const */ 
    void send(const std::string& s) throw (invalid_communicator) 
    { 
     try { 
      auto sh = std::shared_ptr<communicator>(comms_); 
      sh->send(s); 
     } catch (const std::bad_weak_ptr& e) { 
      comms_ = decltype(comms_)(); 
      throw invalid_communicator(s); 
     } 
    } 

private: 
    std::weak_ptr<communicator> comms_; 
}; 

int main() { 
    int rv = -1; 
    sender mysender; 

    for (auto com : {1, 2, 3}) { 
     try { 
      { 
       // create comms object 
       auto comms = std::make_shared<communicator>(
        "myname", com, 9600 
       ); 
       mysender.set_communicator(comms); 
       mysender.send("Hi guys!"); 
      }// comms object gets deleted here 

      mysender.send("Hi guys after ptr delete!"); 

      // never reached in this example; just to illustrate 
      // how the story could continue 
      rv = EXIT_SUCCESS;    
      break; // it'd be not nice to "break", but I did not want to 
        // introduce another state variable 
     } catch (const sender::invalid_communicator& e) { 
      std::cerr << e.what() << std::endl; 
     } 
    } 

    return rv; 
} 

live di un Coliru

1

E 'questo il modo corretto di utilizzare puntatori std intelligenti per garantire la sicurezza ptr

in aggiunta a ciò che è stato menzionato da decltype_auto, posso solo aggiungere che il motivo dell'uso di weak_ptr è tra gli altri per prevenire le dipendenze cicliche. Potresti anche renderlo condiviso se questa possibilità non esiste, il che renderebbe l'implementazione di inviare meno errori inclini, eccetto se la vita del chanel di comunicazione è davvero temporanea.

È possibile nascondere il fatto che esistono varie connessioni o sessioni nell'implementazione.

1

Inoltre, quando si progettano interfacce/API con puntatori intelligenti standard, considerare l'uso dei puntatori più limitati come unique_pointer.

Tale puntatore comunica molto chiaramente l'intento - ad es. prendendo un puntatore univoco come argomento di una funzione, si indica chiaramente all'utente che sta cedendo la proprietà della risorsa puntata alla funzione chiamata.

Problemi correlati