Domanda

Supponiamo che io abbia il seguente C++:

char *p = new char[cb];
SOME_STRUCT *pSS = (SOME_STRUCT *) p;
delete pSS;

È sicuro secondo lo standard C++?Devo tornare a a char* e poi utilizzare delete[]?So che funzionerà nella maggior parte dei compilatori C++, perché sono dati semplici, senza distruttori.È garantito che sia sicuro?

È stato utile?

Soluzione

Non è garantito che sia sicuro.Ecco un collegamento pertinente nelle FAQ lite di C++:

[16.13] Posso abbandonare il [] quando si elimina un array di qualche tipo integrato (char, int, eccetera.)?

http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.13

Altri suggerimenti

No, è un comportamento indefinito: un compilatore potrebbe plausibilmente fare qualcosa di diverso e, come dice la voce FAQ di C++, tonfo collegato a dice, operator delete[] potrebbe essere sovraccarico per fare qualcosa di diverso operator delete.A volte puoi farla franca, ma è anche una buona pratica prendere l'abitudine di abbinare delete[] con new[] nei casi in cui non puoi.

Ne dubito fortemente.

Esistono molti modi discutibili per liberare memoria, ad esempio è possibile utilizzare delete sul tuo char array (anziché delete[]) e probabilmente funzionerà bene.IO bloggato in dettaglio su questo (mi scuso per il collegamento automatico, ma è più semplice che riscrivere tutto).

Il problema non è tanto il compilatore quanto la piattaforma.La maggior parte delle librerie utilizzerà i metodi di allocazione del sistema operativo sottostante, il che significa che lo stesso codice potrebbe comportarsi diversamente su Mac rispetto a Mac.Finestre controLinux.Ho visto esempi di questo e ognuno era un codice discutibile.

L'approccio più sicuro consiste nell'allocare e liberare memoria utilizzando sempre lo stesso tipo di dati.Se stai assegnando chars e restituendoli ad altro codice, potrebbe essere meglio fornire metodi di allocazione/deallocazione specifici:

SOME_STRUCT* Allocate()
{
    size_t cb; // Initialised to something
    return (SOME_STRUCT*)(new char[cb]);
}

 

void Free(SOME_STRUCT* obj)
{
    delete[] (char*)obj;
}

(Sovraccarico di new E delete anche gli operatori potrebbero essere un'opzione, ma non mi è mai piaciuto farlo.)

Lo standard C++ [5.3.5.2] dichiara:

Se l'operando ha un tipo di classe, l'operando viene convertito in un tipo di punta chiamando la funzione di conversione sopra menzionata e l'operando convertito viene utilizzato al posto dell'operando originale per il resto di questa sezione.In entrambe le alternative, il valore dell'operando di eliminazione può essere un valore null puntatore. Se non è un valore null puntatore, nella prima alternativa (oggetto elimina), il valore dell'operando di eliminazione deve essere un puntatore a un oggetto non arrogante o un puntatore a un subject (1.8) che rappresenta una classe base di tale un oggetto (clausola 10).In caso contrario, il comportamento non è definito.Nella seconda alternativa (elimina array), il valore dell'operando di eliminazione deve essere il valore del puntatore risultante da una nuova array di nuova espressione.77) In caso contrario, il comportamento non è definito.[ Nota:Ciò significa che la sintassi dell'espressione di eliminazione deve corrispondere al tipo di oggetto allocato da nuovo, non dalla sintassi della nuova espressione.—nota finale] [ Nota:Un puntatore a un tipo const può essere l'operando di un'espressione di eliminazione;Non è necessario gettare via la costine (5.2.11) dell'espressione del puntatore prima che venga utilizzata come operando dell'espressione di eliminazione.—nota finale]

Questa è una domanda molto simile a quella a cui ho risposto qui: testo del collegamento

In breve no, non è sicuro secondo lo standard C++.Se, per qualche motivo, hai bisogno di un oggetto SOME_STRUCT allocato in un'area di memoria che ha una dimensione diversa da size_of(SOME_STRUCT) (ed è meglio che sia più grande!), allora è meglio usare una funzione di allocazione grezza come globale operator new per eseguire l'allocazione e quindi creare l'istanza dell'oggetto in memoria grezza con un posizionamento new.Posizionamento new sarà estremamente economico se il tipo di oggetto non ha un costruttore.

void* p = ::operator new( cb );
SOME_STRUCT* pSS = new (p) SOME_STRUCT;

// ...

delete pSS;

Funzionerà per la maggior parte del tempo.Dovrebbe funzionare sempre se SOME_STRUCT è una struttura POD.Funzionerà anche in altri casi se SOME_STRUCTIl costruttore di non lancia un'eccezione e if SOME_STRUCT non dispone di un'eliminazione operatore personalizzata.Questa tecnica elimina anche la necessità di eventuali calchi.

::operator new E ::operator delete sono l'equivalente più vicino del C++ a malloc E free e poiché questi (in assenza di sostituzioni di classe) vengono chiamati in modo appropriato da new E delete espressioni che possono (con attenzione!) essere usate in combinazione.

Mentre questo Dovrebbe funziona, non penso che tu possa garantire che sia sicuro perché SOME_STRUCT non è un char* (a meno che non sia semplicemente un typedef).

Inoltre, poiché stai utilizzando diversi tipi di riferimenti, se continui a utilizzare l'accesso *p e la memoria è stata eliminata, riceverai un errore di runtime.

Funzionerà correttamente se si punta alla memoria E il puntatore con cui stai puntando sono entrambi POD.In questo caso, nessun distruttore verrebbe comunque chiamato e l'allocatore di memoria non conosce o non si preoccupa del tipo archiviato nella memoria.

L'unico caso in cui questo va bene con i tipi non POD è se il puntatore è un sottotipo del puntatore (ad es.Stai puntando su un'Auto con un Veicolo*) e il distruttore del puntatore è stato dichiarato virtuale.

Questo non è sicuro, e finora nessuna delle risposte ha sottolineato abbastanza la follia di farlo.Semplicemente non farlo, se ti consideri un vero programmatore, o se desideri lavorare come programmatore professionista in un team.Puoi solo dire che la tua struttura contiene un non distruttore al momento, tuttavia stai creando una brutta trappola forse per il compilatore e specifica del sistema per il futuro.Inoltre, è improbabile che il tuo codice funzioni come previsto.Il meglio che puoi sperare è che non si blocchi.Tuttavia sospetto che otterrai lentamente una perdita di memoria, poiché le allocazioni di array tramite new molto spesso allocano memoria extra nei byte precedente al puntatore restituito.Non libererai la memoria che pensi di essere.Una buona routine di allocazione della memoria dovrebbe rilevare questa discrepanza, così come strumenti come Lint ecc.

Semplicemente non farlo ed elimina dalla tua mente qualunque processo di pensiero ti abbia portato anche solo a considerare tali sciocchezze.

Ho modificato il codice per utilizzare malloc/free.Anche se so come MSVC implementa new/delete per plain-old-data (e SOME_STRUCT in questo caso era una struttura Win32, quindi semplice C), volevo solo sapere se era una tecnica portatile.

Non lo è, quindi userò qualcosa che lo sia.

Se usi malloc/free invece di new/delete, malloc e free non si preoccuperanno del tipo.

Quindi, se stai utilizzando un POD simile a C (vecchi dati semplici, come un tipo incorporato o una struttura), puoi eseguire il malloc di un tipo e liberarne un altro. nota che questo è uno stile scadente anche se funziona.

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