Newsgroup: it.comp.lang.c++
Titolo: Eccezione durante allocazione

Salve,
sono incappato in un problema che non riesco a comprendere.
Ho un buffer che viene deallocato senza problemi ( ed il suo valore 
posto a 0 per rilevarne la 'disponibilita' ); al ritorno dal metodo 
Deallocate() che compie quest'operazione - che viene eseguito senza 
problemi - provo a fare un'allocazione sullo stesso buffer con 
un'istruzione molto semplice, del genere:
_elements = new Type[ _capacity ];
nella seguente condizione:
_elements e' Type *
_capacity = 2
Ottengo un errore in fase di runtime del tipo:
C++ exception: std::bad_alloc @ 0x0012f8e4.
Tutto questo accade nel metodo:
template< typename Type >
bool Vector< Type >::Allocate( bool Append = false, bool Zero = true )
che in pratica dealloca se ce n'è bisogno, e poi alloca.
Sto usando il compilatore VC++ 2003 (giusto per la cronaca, il codice in 
esame e' tutto appartenente al C++ Standard), magari siete al corrente 
di qualche workaround....oppure c'entrano i template nella gestione 
*non* standard del VC++ 2003 ?
Grazie per ogni eventuale aiuto,
    Bruno Ripa

Bruno Ripa ha scritto:

Aggiungo che il template Vector<> funziona con i tipi primitivi ( tutto 
nasce dal fatto che con tipi custom alla seconda chiamata di Add() mi 
capita quell'eccezione ).
La struttura vector la sto utilizzando per memorizzare oggetti a loro 
volta template (Vector3<float>, vettori di R^3). La combinanzione delle 
due cose potrebbe dare fastidio, ma non riesco a capire come.

 C++ exception: std::bad_alloc @ 0x0012f8e4.
Questa exception accade normalemente quando non c'è abbastanza
memoria. O piùtosto quando il sisteme non riesce a trovare la
memoria richiesta. Se sei sicuro che la memoria c'è, e possibile
anche che hai corrotto il "free space arena". Un caso tipico:
scrivi troppo lontano in un buffer allocato, che spesso non fà
probleme ch'a un'allocazione ulteriore.
--
James Kanze        GABI Software         mailto:
james...@free.fr
Conseils en informatique orientée objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, 
+33 (0)1 30 23 00 34

Il punto e' che io neanche ci scrivo, lo dealloco e provo a riallocarlo, 
e qui crasha....
kanze ha scritto:



Prova a postare una frammento di codice piu' corposo e forse qualcuno ti 
potra' aiutare.
Il problema protrebbe essere in una o due linee di codice superiori a quelle 
dove si verifica l'eccezione
Fabio 

Il metodo Add che aggiunge elementi nel vettore
template< typename Type >
bool Vector< Type >::Add( const Type & Object )
{
	if ( _size != _capacity )
	{
		_elements[ _size ] = Object;
		++_size;				
	}
	else
	{
		Type * tmp = NULL;
		_capacity+=_growth;
		_size++;
 
		// Salvo il vettore *precedente*
		if ( _size - 1 != 0 )
		{
			tmp = new Type [ _size - 1 ];
			memcpy( tmp, _elements, sizeof( Type ) * _size - 1);
		}
 
		Allocate();	<-- Qui il crash
 
		if ( _size - 1 != 0 )
		{			
			memcpy( _elements, tmp, sizeof( Type ) * _size - 1 );
 
			delete [] tmp;
			tmp = 0;
		}
 
		_elements[ _size-1 ] = Object;
	}
 
	return true;
}
template< typename Type >
bool Vector< Type >::Allocate( bool Append = false, bool Zero = true )
{
	if ( _capacity != 0 )
	{
		Deallocate( true );
		if ( ! Append )
		{
			_elements = new Type[ _capacity ];   <-- Questa e' l'istruzione del crash
		}
		else
		{		
			// Questa parte per ora non e' mai stata eseguita quindi non c'è
		}
	}
	return true;
}
template< typename Type >
void Vector< Type >::Deallocate( bool Lazy = false )
{
	if ( _elements )
	{
		delete [] _elements;
		_elements = 0;
		if ( ! Lazy )
		{			
			_size = 0;
			_capacity = 0;
		}
	}	
}
_elements e' dichiarata cosi':
Type * _elements;
il resto:
int _size;	// Elementi presenti
int _capacity;	// Numero di elementi per cui e' gia' allocato lo spazio
int	_growth;// Step di crescita: numero di elementi aggiunti ogni volta 
che manca spazio (cerca di minimizzare le allocazioni dinamiche)
Purtoppo sono un macello di classi, non posso postare tutto. Credo che 
pero' sia interessante il fatto che se allora un Vector di - esempio - 
interi il tutto funziona bene, se invece come ho fatto creo un Vector di 
oggetti miei (una classe template) alla seconda iterazione crasha.
FabioAng ha scritto:


>  memcpy( tmp, _elements, sizeof( Type ) * _size - 1);
forse è qui l'errore
 memcpy( tmp, _elements, sizeof( Type ) * (_size - 1));
P.S. in tutti i casi in cui devi testare una dimensione usa
if(_size < _capacity)
invece di
if ( _size != _capacity )
--  
Postato da Virgilio Newsgroup: scopri i nuovi gruppi 
Eros, foto e video a 
http://newsgroup.eros.virgilio.it


Il problema *potrebbe* essere la memcpy()
Se Type e' un tipo primitivo (int,long,...) oppure un POD tutto ok.
Ma supponiamo che Type sia per esempio una classe stringa (std::string) che 
notoriamente contiene un puntatore a una struttura (di solito). Eccoti i 
passaggi:
a) Viene allocata una zona di memoria per contenere il vecchio vettore 
(punto1)
b) Viene copiato il contenuto del vecchio nel vettore puntato da 'tmp'. 
Attenzione la memcpy() copia in modalita' bitwise quindi 'tmp' ed 
'_elements' puntano a locazioni diverse mentre i riferimenti interni della 
classe string sono gli stessi.
c) La funzione Allocate dealloca '_elements' e chiamando i distruttori dei 
singoli oggetti std::string libera i riferimenti di memoria delle singole 
classi (punto3)
d) viene riallocata la memoria per contenere il nuovo vettore in '_elements'
e) Viene copiata la zona di memoria puntata da 'tmp', dove dobrebbe esserci 
il contenuto del vecchio puntatore, sul nuovo puntatore. Ma i riferimenti a 
cui 'tmp' ed '_elements' puntavano sono gia' stati liberati (punto3).
Anche in questo caso e' sconsigliato l'uso della memcpy()
Per verificare se il problema e' la memcpy() ti consiglio di lasciare il 
codice cosi' come e' e farti una funzione interna alla classe che esegue una 
copia iterando sul vettore e usando l'operatore di copia della stessa classe 
Type.
Spero di esserti stato di aiuto.
Ciao
Fabio 

Anche io ci stavo pensando....appena posso ti faccio sapere.
Grazie dell'aiuto !
FabioAng ha scritto:


 }
Argh! Come ti ha detto Fabio, questo funziona solo con i POD. Con gli altri
tipi, accadono cose orrende.
Quindi: o fai come ha proposto Fabio: cioè iteri e copi gli oggetti usando
l'operatore assegnamento, oppure allochi memoria non inizializzata e
crei/copi gli oggetti usando il placement new.
La seconda opzione, in generale, è più efficiente, perché gli oggetti sono
direttamente inizializzati con i valori desiderati.
Non capisco perché non usi std::vector. C'è qualche motivo?
Max

Grazie a tutti per i suggerimenti, come da voi ipotizzato memcpy si 
comporta da pelle d'oca con i tipi custom.
Curiosita': POD per cosa sta ? Suppongo sia una sigla per indicare i 
tipi non primitivi, ma esattamente ?
Bruno Ripa ha scritto:


 tipi non primitivi, ma esattamente ?
POD significa "Plain Old Data". Un tipo è POD se è un tipo built-in oppure
se è una struct o union priva di costruttori non banali e contenente
soltanto membri POD.
Max