Domanda

Supponiamo che io ho il seguente codice:

void* my_alloc (size_t size)
{
   return new char [size];
}

void my_free (void* ptr)
{
   delete [] ptr;
}

Questa funzione è sicura? O deve ptr essere gettato a char* prima eliminazione?

È stato utile?

Soluzione

Dipende "sicuro". E di solito funziona perché le informazioni sono memorizzate insieme con il puntatore sulla allocazione in sé, in modo che il deallocatore può tornare al posto giusto. In questo senso è "sicuro" fino a quando l'allocatore utilizza tag di confine interne. (Molti lo fanno.)

Tuttavia, come detto in altre risposte, l'eliminazione di un puntatore nullo non sarà chiamare i distruttori, che può essere un problema. In questo senso, non è "sicuro".

Non v'è alcuna buona ragione per fare quello che stai facendo il modo in cui si sta facendo. Se si desidera scrivere le proprie funzioni deallocazione, è possibile utilizzare i modelli di funzione per generare funzioni con il tipo corretto. Un buon motivo per farlo è quello di generare allocatori piscina, che possono essere estremamente efficiente per tipi specifici.

Come già detto in altre risposte, questo è indefinito comportamento in C ++. In generale, è bene evitare un comportamento indefinito, anche se l'argomento in sé è complesso e pieno di opinioni contrastanti.

Altri suggerimenti

L'eliminazione tramite un puntatore nullo non è definito dal ++ standard C - vedi sezione 5.3.5 / 3:

  

Nella prima alternativa (cancellare   oggetto), se il tipo statico del   operando è diverso dal suo dinamico   tipo, tipo statico è una base   classe di tipo dinamico della operando   e il tipo statico deve avere una   distruttore virtuale o il comportamento è   non definito. Nella seconda alternativa   (Cancella array) se il tipo dinamico di   l'oggetto da eliminare differisce dalla   il tipo statico, il comportamento è   non definito.

E la sua nota:

  

Ciò implica che un oggetto non può essere   eliminato utilizzando un puntatore di tipo void *   perché non ci sono oggetti di tipo   vuoto

.

Non è una buona idea e non qualcosa che si farebbe in C ++. Si stanno perdendo le tue informazioni tipo per nessun motivo.

Il distruttore non sarà chiamato sugli oggetti nel vostro array che si sta Cancellazione quando lo si chiama per i tipi non primitivi.

Si dovrebbe invece ignorare nuova / delete.

L'eliminazione del vuoto * probabilmente liberare la memoria in modo corretto per caso, ma è sbagliato, perché i risultati sono indefiniti.

Se per qualche motivo a me sconosciuto è necessario memorizzare il puntatore in un vuoto * poi liberarla, si dovrebbe usare malloc e gratuito.

L'eliminazione di un puntatore nullo è pericoloso perché non distruttori saranno chiamati sul valore in realtà punta a. Ciò può causare perdite di memoria / risorsa nell'applicazione.

La domanda non ha senso. La vostra confusione può essere in parte dovuto al popolo lingua sciatta utilizzano spesso con delete:

Si utilizza delete per distruggere un oggetto che è stato assegnato dinamicamente. Non fare così, si forma un'espressione Elimina con puntatore a tale oggetto . Non si può mai "elimina un puntatore". Quello che è davvero fare è "eliminare un oggetto, che è identificato dal suo indirizzo".

Ora vediamo il motivo per cui la questione non ha senso: Un puntatore nullo non è il "indirizzo di un oggetto". E 'solo un indirizzo, senza alcuna semantica. E ' possono sono venuti da l'indirizzo di un oggetto reale, ma che l'informazione è persa, perché è stato codificato nel tipo del puntatore originale. L'unico modo per ripristinare un puntatore oggetto è quello di lanciare il puntatore nullo di nuovo ad un puntatore di oggetto (che richiede l'autore di sapere che cosa significa il puntatore). void sé un tipo incompleto e quindi mai il tipo di un oggetto, e un puntatore nullo può mai essere utilizzato per identificare un oggetto. (Gli oggetti sono identificati congiuntamente dal loro tipo e indirizzo.)

A causa char non ha alcuna logica speciale distruttore. Questo non funziona.

class foo
{
   ~foo() { printf("huzza"); }
}

main()
{
   foo * myFoo = new foo();
   delete ((void*)foo);
}

Il d'ctor non verrà chiamato.

Se si desidera utilizzare * vuoto, perché non si utilizza solo malloc / libero? new / delete è più di una semplice gestione della memoria. In sostanza, new / delete chiamate un costruttore / distruttore e ci sono più cose in corso. Se si utilizza solo tipi built-in (come char *) e li elimina attraverso void *, che avrebbe funzionato, ma ancora non è raccomandato. La linea di fondo è l'uso malloc / gratuito se si desidera utilizzare void *. In caso contrario, è possibile utilizzare le funzioni di template per la vostra convenienza.

template<typename T>
T* my_alloc (size_t size)
{
   return new T [size];
}

template<typename T>
void my_free (T* ptr)
{
   delete [] ptr;
}

int main(void)
{
    char* pChar = my_alloc<char>(10);
    my_free(pChar);
}

Se proprio deve fare questo, perché non tagliare fuori l'uomo medio (il new e operatori delete) e chiamare il operator new globale e operator delete direttamente? (Naturalmente, se si sta cercando di strumento gli operatori new e delete, in realtà si dovrebbe reimplementare operator new e operator delete.)

void* my_alloc (size_t size)
{
   return ::operator new(size);
}

void my_free (void* ptr)
{
   ::operator delete(ptr);
}

Si noti che a differenza malloc(), operator new getta std::bad_alloc in caso di fallimento (o chiama il new_handler se si è registrato).

Un sacco di persone hanno già commentato dicendo che no, non è sicuro per eliminare un puntatore nullo. Sono d'accordo con questo, ma volevo anche aggiungere che se si sta lavorando con puntatori void al fine di allocare le matrici contigui o qualcosa di simile, che si può fare questo con new in modo che sarete in grado di utilizzare in modo sicuro delete (con , ahem, un po 'di lavoro extra). Questo viene fatto assegnando un puntatore nullo alla regione di memoria (chiamato 'un'arena') e quindi fornendo il puntatore al campo per nuovo. Vedere questa sezione nella C ++ FAQ . Questo è un approccio comune per attuare pool di memoria in C ++.

E 'difficile trovare una ragione per farlo.

Prima di tutto, se non si conosce il Tipo dei dati, e tutto quello che sappiamo è che si tratta di void*, allora si dovrebbe davvero essere solo trattando i dati come un senza tipo blob di dati binari (unsigned char*), e utilizzare malloc / free a che fare con esso. Questo è necessario a volte per cose come i dati delle forme d'onda e simili, in cui è necessario passare intorno puntatori void* a C API. Questo va bene.

Se fare conoscere il tipo di dati (cioè ha un ctor / dtor), ma per qualche ragione si è conclusa con un puntatore void* (per qualsiasi motivo avete) allora si dovrebbe davvero gettato di nuovo al tipo di sapere che sia , e chiamare delete su di esso.

Ho usato void *, (aka tipi sconosciuti) nel mio quadro, mentre nel codice riflessione e di altri talenti di ambiguità, e finora, non ho avuto problemi (perdita di memoria, le violazioni di accesso, etc.) da qualsiasi compilatori . Solo gli avvertimenti a causa del funzionamento di essere non standard.

Si rende perfettamente senso per eliminare uno sconosciuto (void *). Basta assicurarsi che il puntatore segue queste linee guida, o può smettere di dare un senso:

1) Il puntatore ignoto non deve puntare ad un tipo che ha una deconstructor banale, e così quando fuso come un puntatore Sconosciuto dovrebbe mai eliminati. eliminare il puntatore sconosciuta Solo dopo la fusione di nuovo nel tipo di originale.

2) è l'istanza viene fatto riferimento come un puntatore sconosciuta nella memoria dello stack legato o heap legato? Se il puntatore sconosciuto fa riferimento a un'istanza in pila, quindi non dovrebbe mai essere cancellato!

3) Sei positivo di 100% il puntatore ignoto è una regione di memoria valida? No, allora non dovrebbe mai essere DELTED!

In tutto, c'è molto poco lavoro diretto che può essere fatto utilizzando un ignoto (void *) tipo di puntatore. Tuttavia, indirettamente, il vuoto * è una grande risorsa per gli sviluppatori C ++ di fare affidamento su quando è richiesta l'ambiguità dei dati.

Se si desidera solo un buffer, utilizzare malloc / libero. Se è necessario utilizzare nuovi / delete, prendere in considerazione una classe wrapper banale:

template<int size_ > struct size_buffer { 
  char data_[ size_]; 
  operator void*() { return (void*)&data_; }
};

typedef sized_buffer<100> OpaqueBuffer; // logical description of your sized buffer

OpaqueBuffer* ptr = new OpaqueBuffer();

delete ptr;

Per il caso particolare di carattere.

char è un tipo intrinseco che non ha un distruttore speciale. Così gli argomenti perdite è un controverso.

sizeof (char) è di solito uno così non c'è argomento allineamento neanche. Nel caso di rara piattaforma in cui il sizeof (char) è non uno, allocano memoria allineato abbastanza per la loro char. Quindi l'argomento di allineamento è anche una discutibile.

malloc / libero sarebbe più veloce su questo caso. Ma si perde std :: bad_alloc e hanno per controllare il risultato di malloc. Chiamare gli operatori new e delete globali potrebbe essere migliore in quanto bypassare l'uomo medio.

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