2013-02-14 7 views
5

Ho un oggetto con alcuni puntatori al suo interno. Il distruttore chiama delete su questi puntatori. Ma a volte voglio eliminarli, ea volte no. Quindi mi piacerebbe poter eliminare l'oggetto senza chiamando il distruttore. È possibile?È possibile eliminare un oggetto in C++ senza chiamare il distruttore?

Modifica: Mi rendo conto che questa è una idea VEDUTA che nessuno dovrebbe mai fare. Tuttavia, voglio farlo perché renderà alcune funzioni interne molto più facili da scrivere.

+0

Quindi la domanda è come rendere l'oggetto ad allocazione dinamica da deallocare in un modo in cui non è necessario chiamare esplicitamente 'delete' /' free'? – LihO

+0

@LihO Sì. In alternativa, chiama 'delete' /' free' senza chiamare il distruttore. –

+5

Bene, basta impostare quei puntatori interni a 'nullptr', quindi 'cancellarli sarà un no-op. –

risposta

7

È possibile impostare i puntatori su NULL, quindi il distruttore non li eliminerà.

struct WithPointers 
{ 
    int* ptr1; 
    int* ptr2; 

    WithPointers(): ptr1(NULL), ptr2(NULL) {} 

    ~WithPointers() 
    { 
     delete ptr1; 
     delete ptr2; 
    } 
} 

... 
WithPointers* object1 = new WithPointers; 
WithPointers* object2 = new WithPointers; 
object1->ptr1 = new int(11); 
object1->ptr2 = new int(12); 
object2->ptr1 = new int(999); 
object2->ptr2 = new int(22); 
... 
int* pointer_to_999 = object2->ptr1; 
object2->ptr1 = NULL; 
delete object1; 
delete object2; // the number 999 is not deleted now! 
// Work with the number 999 
delete pointer_to_999; // please remember to delete it at the end! 
+0

E va in boom in futuro quando dimentica. Usa la forza e fai attenzione al lato oscuro! –

+0

Avrei dovuto pensare a questo semplice trucco. Grazie. –

+1

@MichaelDorgan: Se va forte se dimentica non è un problema: lo scoprirà e lo correggerà. Il "lato oscuro" sono cose che non sono corrette ma che non vanno a gonfie vele! (non lo saprai mai fino a quando un utente un giorno vedrà qualcosa di sbagliato) –

4

Eww! Sì, è possibile, no non lo farei. È necessario controllare gli oggetti che richiedono una durata variabile tramite un puntatore condiviso o un altro oggetto con conteggio di riferimento. Cleaner funziona con C++ piuttosto che rompere alcuni dei suoi inquilini interni ...

+0

So che questo è il modo giusto per farlo, ma mi piacerebbe farlo a modo mio per vari motivi. Questa non è una funzionalità che sarebbe esposta pubblicamente, ma qualcosa usata internamente per rendere alcune funzioni molto più semplici. –

+2

Ti consiglierei di non andare lì. Chissà, quando in futuro, qualcun altro entrerà nel tuo codice e tratterà qualcosa e boom. O forse una nuova versione del compilatore, ecc. Rompere le regole rende molto difficile eseguire il debug di qualcosa, poiché la soluzione non sarà la prima cosa che verrà in mente quando si guarda il codice. –

+2

Potrebbe rendere alcune funzioni interne "più facili da scrivere" (discutibili) ma * certamente * non le renderà più sicure o più facili da * mantenere *. Chiunque guardi questo codice dopo aver pensato che tu fossi pazzo. – meagar

2

Qualsiasi cosa tu stia effettivamente cercando di fare, c'è un problema con il tuo codice. Se non si desidera eliminare gli oggetti secondari, utilizzare solo un flag booleano che verrà impostato prima di eliminare l'oggetto e che verrà considerato dal distruttore.

Ma sinceramente, dovresti usare puntatori intelligenti invece di puntatori nudi (e nel tuo caso, sembra che i puntatori condivisi siano ciò di cui hai bisogno).

1

Come altri hanno suggerito, il modo corretto per risolvere questo problema non è NON chiamare il distruttore [è sufficiente aggiungere qualcosa come std::string o std::vector al proprio oggetto e all'improvviso si ha una perdita di memoria] . Il modo corretto è quello di non lasciare che il proprio oggetto possieda tutti gli altri oggetti (ad esempio cancellarli separatamente prima/dopo che l'oggetto sia stato eliminato), o avere un metodo con cui l'oggetto sa se eliminare i puntatori o meno.

2

Scrivere un metodo che è possibile chiamare prima di chiamare il distruttore. Il metodo capovolgerà un membro booleano. Quando viene chiamato il destuctor, controllerà quel membro booleano e distruggerà il puntatore se è vero, e lo manterrà se è falso.

Non consiglierei di farlo. Meglio che la classe non si assuma la responsabilità dell'eliminazione del puntatore.

1

Sì, questo è possibile. std::vector fa questo, in quanto assegna i buffer con lo spazio per gli oggetti, quindi li costruisce condizionalmente (sul posto) e li distrugge, gestendo la memoria indipendentemente dalla durata dell'oggetto.

In C++ 11, utilizzerei un union del tipo e un tipo piccolo con costruttori/distruttori banali per indicare un percorso di memoria che può adattarsi al tipo, ma non deve avere quel tipo in esso . Esterno a quello che devi tracciare se l'oggetto è effettivamente lì. La creazione dell'articolo consiste nell'utilizzare il posizionamento new e distruggerlo consiste nel chiamare manualmente il distruttore.

Il buffer degli oggetti union, sia N oggetti o 1, sarebbe gestito in modo completamente indipendente. Il costruttore predefinito di union non costruisce nulla, o costruisce il tipo banale (nel qual caso potresti voler distruggere quel tipo banale).

Tuttavia, le probabilità sono che la vera risposta alla tua domanda è "non farlo". E se lo fai, avvolgi i puntatori in un class il cui lavoro solo sta gestendo il pasticcio di cui sopra.Classi di quel tipo (il cui compito è gestire la durata del puntatore e le proprietà del puntatore) sono chiamate "puntatori intelligenti".

7

L'operatore delete fa due cose per l'oggetto si passa:

  • chiama il distruttore appropriata.
  • chiama la funzione di deallocazione, operator delete.

Quindi l'eliminazione di un oggetto senza chiamare un distruttore significa che si desidera chiamare semplicemente operator delete sull'oggetto:

Foo *f = new Foo; 
operator delete(f); 

operator delete è una normale chiamata di funzione e la risoluzione solita ricerca del nome e il sovraccarico è fatto. Tuttavia l'operatore delete ha le sue regole per trovare il corretto operator delete. Ad esempio, l'operatore delete cercherà le funzioni membro operator delete che la solita ricerca del nome e la risoluzione di sovraccarico non trovano. Ciò significa che devi assicurarti di chiamare la funzione corretta quando usi manualmente lo operator delete.

Come dici tu, utilizzando operator delete è direttamente un'idea terribile. Non riuscendo a chiamare il distruttore si interrompe RAII, con conseguente perdita di risorse. Può anche portare a comportamenti indefiniti. Inoltre, dovrai assumerti la responsabilità di scrivere codice eccezionalmente sicuro senza RAII, che è eccezionalmente difficile. Sei quasi sicuro di sbagliare.

+0

'delete f' è in realtà' f-> ~ Foo(); operator delete (f); '(Proprio come' new Foo' è in realtà 'nuovo (operatore new (sizeof (Foo))) Foo'). Quelle forme "* disgiunte allocazione/rilascio da costruire/distruggere *" sono rare, ma si verificano quando la storia della memoria non segue la storia dell'oggetto che detiene. Questo è tipicamente trovato - per esempio - nelle implementazioni di 'std :: vector'. –

+0

@EmilioGaravaglia Credo che stai solo ripetendo una parte di quello che ho detto. Non vedo quali, se del caso, correzioni che stai proponendo. – bames53

+0

Non è una correzione: solo un complemento del corrispondente posizionamento nuovo/operatore nuovo, e su questo costrutto avviene regolarmente all'interno della libreria standard –

Problemi correlati