Domanda

Sono un programmatore C che cerca di capire il C ++. Molti tutorial dimostrano l'istanza di oggetti usando uno snippet come:

Dog* sparky = new Dog();

che implica che in seguito lo farai:

delete sparky;

che ha senso. Ora, nel caso in cui l'allocazione dinamica della memoria non sia necessaria, c'è qualche motivo per usare quanto sopra anziché

Dog sparky;

e lasciare che il distruttore venga chiamato quando lo scintillio esce dal campo di applicazione?

Grazie!

È stato utile?

Soluzione

Al contrario, dovresti sempre preferire le allocazioni di stack, nella misura in cui, come regola generale, non dovresti mai avere nuovo / cancellare nel tuo codice utente.

Come dici tu, quando la variabile viene dichiarata nello stack, il suo distruttore viene automaticamente chiamato quando esce dall'ambito, che è il tuo strumento principale per tenere traccia della durata delle risorse ed evitare perdite.

Quindi, in generale, ogni volta che è necessario allocare una risorsa, che si tratti di memoria (chiamando nuovo), handle di file, socket o altro, avvolgerla in una classe in cui il costruttore acquisisce la risorsa e il distruttore la rilascia . Quindi puoi creare un oggetto di quel tipo nello stack e hai la garanzia che la tua risorsa viene liberata quando esce dall'ambito. In questo modo non è necessario tenere traccia delle coppie nuove / eliminate ovunque per evitare perdite di memoria.

Il nome più comune per questo idioma è RAII

Cerca anche nelle classi di puntatori intelligenti che vengono utilizzate per avvolgere i puntatori risultanti nei rari casi in cui devi allocare qualcosa con nuovo all'esterno di un oggetto RAII dedicato. Passa invece il puntatore a un puntatore intelligente, che quindi tiene traccia della sua durata, ad esempio mediante il conteggio dei riferimenti, e chiama il distruttore quando l'ultimo riferimento esce dall'ambito. La libreria standard ha std :: unique_ptr per una semplice gestione basata sull'ambito e std :: shared_ptr che fa riferimento al conteggio per implementare la proprietà condivisa.

  

Molti tutorial dimostrano l'oggetto   istanza usando uno snippet come ...

Quindi quello che hai scoperto è che la maggior parte dei tutorial fa schifo. ;) La maggior parte dei tutorial ti insegna pratiche pessime in C ++, incluso chiamare new / delete per creare variabili quando non è necessario e darti difficoltà nel tenere traccia della durata delle tue allocazioni.

Altri suggerimenti

Anche se avere le cose in pila potrebbe essere un vantaggio in termini di allocazione e liberazione automatica, presenta alcuni svantaggi.

  1. Potresti non voler allocare enormi oggetti sullo Stack.

  2. Invio dinamico! Considera questo codice:

#include <iostream>

class A {
public:
  virtual void f();
  virtual ~A() {}
};

class B : public A {
public:
  virtual void f();
};

void A::f() {cout << "A";}
void B::f() {cout << "B";}

int main(void) {
  A *a = new B();
  a->f();
  delete a;
  return 0;
}

Questo stamperà "B". Ora vediamo cosa succede quando si utilizza Stack:

int main(void) {
  A a = B();
  a.f();
  return 0;
}

Questo stamperà "A", che potrebbe non essere intuitivo per coloro che hanno familiarità con Java o altri linguaggi orientati agli oggetti. Il motivo è che non hai più un puntatore a un'istanza di B . Invece, un'istanza di B viene creata e copiata in una variabile di tipo A .

Alcune cose potrebbero accadere in modo non intuitivo, specialmente quando sei nuovo in C ++. In C hai i tuoi puntatori e basta. Sai come usarli e fanno sempre lo stesso. In C ++ questo non è il caso. Immagina cosa succede, quando usi a in questo esempio come argomento per un metodo: le cose si complicano e fa una differenza enorme se a è di tipo A o A * o anche A & amp; (chiamata per riferimento). Sono possibili molte combinazioni e si comportano tutte diversamente.

Bene, il motivo per usare il puntatore sarebbe esattamente lo stesso del motivo per usare i puntatori in C allocati con malloc: se vuoi che il tuo oggetto viva più a lungo della tua variabile!

Si consiglia vivamente di NON utilizzare il nuovo operatore se è possibile evitarlo. Soprattutto se usi le eccezioni. In generale è molto più sicuro lasciare che il compilatore liberi i tuoi oggetti.

Ho visto questo anti-pattern da persone che non ottengono abbastanza & amp; indirizzo dell'operatore. Se devono chiamare una funzione con un puntatore, si allocano sempre sull'heap in modo da ottenere un puntatore.

void FeedTheDog(Dog* hungryDog);

Dog* badDog = new Dog;
FeedTheDog(badDog);
delete badDog;

Dog goodDog;
FeedTheDog(&goodDog);

Tratta l'heap come un immobile molto importante e usalo con giudizio. La regola di base del pollice è usare lo stack quando possibile e usare heap ogni volta che non c'è altro modo. Allocare gli oggetti in pila è possibile ottenere molti vantaggi come:

(1). Non è necessario preoccuparsi del riavvolgimento dello stack in caso di eccezioni

(2). Non devi preoccuparti della frammentazione della memoria causata dall'allocazione di più spazio di quanto necessario dal tuo gestore di heap.

L'unica ragione di cui mi preoccuperei è che Dog ora è allocato in pila, piuttosto che nell'heap. Quindi se Dog ha dimensioni di megabyte, potresti avere un problema,

Se è necessario seguire il percorso nuovo / elimina, fare attenzione alle eccezioni. E per questo motivo dovresti usare auto_ptr o uno dei tipi di puntatore intelligente boost per gestire la durata dell'oggetto.

Non c'è motivo per new (nell'heap) quando è possibile allocare nello stack (a meno che per qualche motivo non si abbia uno stack piccolo e si desideri utilizzare l'heap.

Potresti prendere in considerazione l'uso di shared_ptr (o una delle sue varianti) dalla libreria standard se desideri allocarlo sull'heap. Gestirà la cancellazione per te una volta che tutti i riferimenti a shared_ptr non saranno più presenti.

C'è un motivo in più, che nessun altro ha menzionato, perché potresti scegliere di creare dinamicamente il tuo oggetto. Gli oggetti dinamici basati su heap ti consentono di utilizzare polimorfismo .

Ho avuto lo stesso problema in Visual Studio. Devi usare:

yourClass- > classmethod ();

anziché:

yourClass.classMethod ();

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top