2011-09-23 7 views
12

(Nota: questa domanda è stata motivata dal tentativo di venire con preprocessore aggiustamenti per generare un'allocazione no-op per rispondere a quest'altra domanda:!E 'ben definito/legale per il posizionamento-nuove più volte allo stesso indirizzo?

Macro that accept new object

... tenete a mente)

Ecco una classe artificiosa:

class foo { 
private: 
    int bar; 
public: 
    foo(int bar) : bar (bar) 
     { std::cout << "construct foo #" << bar << std::endl; } 
    ~foo() 
     { std::cout << "destruct foo #" << bar << std::endl; } 
}; 

... che mi assegnerà in questo modo:

// Note: for alignment, don't use char* buffer with new char[sizeof(foo)] ! 
void* buffer = operator new(sizeof(foo)); 

foo* p1 = new (buffer) foo(1); 
foo* p2 = new (buffer) foo(2); 

/* p1->~foo(); */ /* not necessary per spec and problematic in gen. case */ 
p2->~foo(); 

Sulla gcc che ho intorno, ho ottenere il risultato "atteso":

construct foo #1 
construct foo #2 
destruct foo #2 

Che è grande, ma potrebbe il compilatore/runtime rifiutare questa come un abuso e di essere ancora sul lato destro delle specifiche?

Che ne dici di filettatura? Se in realtà non ci interessa il contenuto di questa classe (diciamo che è solo un oggetto fittizio) almeno non si bloccherà, come nell'applicazione ancora più semplice che ha motivato questo con un POD int?

+0

Sono abbastanza sicuro (ma non ho riferimenti) che questo non è kosher per gli oggetti non POD. Non sono sicuro del POD ... – bdonlan

+0

Penso che intendessi "foo * p1 = new (buffer) foo (1);" –

+0

Il distruttore non modifica lo stato dell'oggetto. La maggior parte dei distruttori non sono così. –

risposta

14

Peforming posizionamento-nuovo più volte sullo stesso blocco di memoria è perfettamente a posto. Inoltre, per quanto strano possa sembrare, non si è nemmeno sicuri di distruggere l'oggetto che già risiede in quella memoria (se presente). Lo standard permette esplicitamente che a 3,8/4

Un programma può finire la durata di qualsiasi oggetto riutilizzando stoccaggio che l'oggetto occupa o chiamando esplicitamente il distruttore per un oggetto di un tipo di classe con un distruttore non banale. Per un oggetto di un tipo di classe con un distruttore non banale, il programma non è necessaria chiamare il distruttore esplicitamente prima della memorizzazione che l'oggetto occupa vengono riutilizzati o rilasciato; [...]

In altre parole, è tua responsabilità prendere in considerazione le conseguenze di non chiamare il distruttore per qualche oggetto.

Tuttavia, non è consentito chiamare il distruttore sullo stesso oggetto due volte come nel codice. Una volta creato il secondo oggetto nella stessa area di memoria, hai effettivamente terminato la durata del primo oggetto (anche se non hai mai chiamato il suo distruttore). Ora hai solo bisogno di distruggere il secondo oggetto.

+0

Freddo. Ho portato fuori il primo distruttore per concentrare la domanda su ciò a cui ero più interessato. Grazie! – HostileFork

+0

Wow! La vaghezza dell'ultimo bit dell'ultima riga della tua citazione mi spaventa un po '- "qualsiasi programma che dipende dagli effetti collaterali prodotti dal distruttore ha un comportamento indefinito". In particolare, cosa significa "dipende" in questo contesto? –

+0

@ Michael Anderson: l'ho già rimosso dalla citazione come irrilevante alla domanda (o almeno così mi è sembrato). Ma non sono esattamente sicuro di cosa significhi. – AnT

2
foo* p1 = new (buffer) foo(1); 
foo* p2 = new (buffer) foo(2); 
p1->~foo(); 
p2->~foo(); 

Stai distruggendo lo stesso oggetto due volte, e che da solo è un comportamento indefinito. La tua implementazione potrebbe decidere di ordinare una pizza quando lo fai, e sarebbe ancora sul lato destro delle specifiche.

Quindi c'è il fatto che il buffer potrebbe non essere allineato correttamente per collocare un oggetto di tipo foo, che è ancora un C++ non standard (secondo C++ 03, penso che C++ 11 rilassi questo).

Aggiornamento: quanto riguarda la questione specificato nel titolo,

E 'ben definito/legale per più volte di collocamento-nuovo allo stesso indirizzo?

Sì, è ben definito per il posizionamento-nuove più volte allo stesso indirizzo, a condizione che punti alla memoria non elaborata.

+0

Come mai dici di ordinare la pizza? :) Sembra che tu abbia fame molta gente. –

+0

La domanda è ancora rilevante anche senza le chiamate del distruttore, in quanto non vi è alcun requisito tecnico per chiamarle. Apparentemente sto cercando la dimostrazione da parte degli avvocati linguistici dei punti correlati. :) – HostileFork

+0

Ordinare la pizza sarebbe un risultato molto più produttivo di quello che fa di solito in quel caso. Voglio vedere i gestori di segnale e/o il supporto del sistema operativo necessari poiché questa sarebbe una grande funzionalità da aggiungere a questo inibitore di gelificazione del sistema di alimentazione che stiamo sviluppando. Siamo riusciti a calzare nella maggior parte dei sistemi operativi su disco fino ad ora ... –

0

No, questo non sembra corretto.

Quando si utilizza il posizionamento new, l'oggetto verrà costruito all'indirizzo l'indirizzo che si passa. In questo esempio si passa lo stesso indirizzo (ad esempio il buffer & [0]) due volte, quindi il secondo oggetto sta semplicemente cancellando il primo oggetto che è già stato costruito in questa posizione.

EDIT: Non penso di capire cosa stai cercando di fare.

Se si dispone di un tipo di oggetto generale (che può avere un/ctor/dtor non banale che può allocare/deallocare risorse) e si cancella il primo oggetto per posizionamento new 'sopra il primo senza prima chiamare esplicitamente il suo distruttore , questo sarà almeno una perdita di memoria.

+0

Vedi l'esempio motivante, e considera il threading: http://stackoverflow.com/questions/7522949/c-macro-that-accent-new-object/7523078#7523078 – HostileFork

+0

@HostileFork: rileggendo le tue modifiche presumo che il tuo oggetto il tipo sarà sempre banale, quindi i dubbi "obliterazione" potrebbero non essere pertinenti ... –

+0

Vedi risposta accettata. Sembra che le doppie chiamate del distruttore possano portare a problemi, ma la cancellazione è ok per spec.Il nuovo posizionamento è una di quelle cose strane che immagino dov'è il compilatore che ti prende in parola! – HostileFork