C ++ puntatori raw per l'archiviazione

3

Sto vedendo un sacco di persone che dicono che non dovresti mai usare puntatori grezzi. Che dire nel caso in cui si memorizza il puntatore raw in una classe, ma il modo in cui vengono popolati è ottenere il puntatore di un oggetto che non è stato creato in modo dinamico? Sono curioso di sapere se questa è una cattiva pratica e se sì come migliorarla. Questo mi sembra ideale dato che non sono obbligato a cancellare qualcosa di così limitato / nessuna possibilità di perdita di memoria? L'idea di "se non l'hai creata non la cancelli".

Posso controllare tutto in base alla portata della variabile che sto passando a destra? Se esce fuori dal campo di applicazione, ciò a cui punta non sarà più valido e genererà un'eccezione se provo ad accedervi e riuscirò a prenderlo e gestirlo. Curioso su pensieri / commenti / dubbi su questo.

class Object
{
private:
   Object* object;
public:
   void Object() { object = NULL; }
   void AddObject(Object* obj)
   {
      object = obj;
   }

  Object* GetObject()
  {
     return object;
  }
};


int main()
{
   Object obj1;
   Object obj2;

   // obj2 wasn't dynamically created so no need to use a smart pointer
   obj1.AddObject(&obj2);

   return 0;
}
    
posta user441521 16.05.2014 - 21:51
fonte

2 risposte

10

Non c'è alcun problema nell'usare i puntatori grezzi per, beh, puntare alle cose - purché tu conosca la durata degli oggetti a cui stai puntando, e che tu abbia qualche garanzia esterna che gli oggetti non scadano mentre tu Ti stai riferendo a loro.

Quali puntatori non dovrebbero essere usati per proprietà , perché il loro tipo non trasmette nulla sulla proprietà o sulla semantica della vita.

La maggior parte delle volte, puoi usare unique_ptr per possedere un oggetto e riferimenti o indicatori grezzi da prendere in prestito da esso; nel caso più raro che tu abbia bisogno di una proprietà condivisa, puoi utilizzare un shared_ptr con weak_ptr per il prestito.

Un po 'di più sulle vite. Dì che hai qualcosa del tipo:

struct Base { virtual ~Base() {} };
struct Derived : Base {};

vector<Base*> bases;
Base* base = new Base();
bases.push_back(base);
delete base;
function(dynamic_cast<Derived&>(*bases[0]));  // won't necessarily throw

Dereferenziare un puntatore il cui referente è stato cancellato non è definito, il che è esattamente ciò che sta accadendo qui. La memoria a cui si riferisce può ancora contenere un oggetto valido, o non può - non si può fare affidamento su alcun comportamento particolare. Considerando che se avessi questo:

vector<shared_ptr<Base>> bases;
shared_ptr<Base> base(make_shared<Base>());
bases.push_back(base);
base.reset();
function(dynamic_cast<Derived&>(*v[0]));  // will throw

Il vettore shared_ptr conserva l'oggetto, rendendolo sicuro di dereferenziare anche dopo che base scade nella chiamata reset() . È sicuro utilizzare un puntatore non elaborato solo quando conosci che l'oggetto sarà vivo quando si dereferenzia il puntatore:

vector<unique_ptr<Base>> bases;
bases.emplace_back(new Base());
bases.emplace_back(new Derived());
{
  vector<Derived*> deriveds;
  for (const auto& base : bases)
    if (const auto derived = dynamic_cast<Derived*>(base.get()))
      deriveds.push_back(derived);
  function(deriveds);  // safe; lifetimes guaranteed by 'bases'
}
    
risposta data 16.05.2014 - 22:31
fonte
2

In questo modo, hai effettivamente legato questi due oggetti insieme. Ora dovrai assicurarti che ogni volta che distruggi obj1 in modo esplicito o implicito, puoi anche distruggere obj2 . Hai creato una struttura dati in cui tutti gli elementi devono essere distrutti contemporaneamente.

Se usi qualcosa come shared_pointer , puoi ottenere la stessa facilità senza questo problema. Sarai in grado di fare la stessa cosa, lasciando che lo scope ripulisca per te, ma non devi preoccuparti di cose che vanno al diavolo se in seguito ti rendi conto che ha senso creare oggetti in ambiti diversi.

È davvero più difficile?

class Object
{
private:
   shared_ptr<Object> object;
public:
   void Object()
   void AddObject(shared_ptr<Object> obj)
   {
      object = obj;
   }

   shared_ptr<Object> GetObject()
   {
      return object;
   }
};


int main()
{
   shared_ptr<Object> obj1(new Object);
   shared_ptr<Object> obj2(new Object);

   obj1.AddObject(&obj2);

   return 0;
}

È essenzialmente uguale al tuo codice, ma senza il requisito di garantire che obj1 e obj2 condividano lo scope.

    
risposta data 16.05.2014 - 22:30
fonte

Leggi altre domande sui tag