Newsgroup: it.comp.lang.c++
Titolo: Stringhe e spreco di memoria

Ciao a tutti,
ho un problema di perdita di memoria (non nel mio cervello ma nella mia
applicazione).
Il problema e´ che mi serve dichiarare statica una stringa in una funzione
per potere aggiungere ad essa pezzi di stringa che passo alla funzione.
Fin qui tutto bene ma poi alla 30-esima volta che chiamo la funzione vorrei
che nella mia stringa vi sia solo uno spazio e lo faccio cosi´ :
include <string>
#define ROWS 30
void add_string( string &little)
{
    static int count = 0;
    static string big(" ");
    if (count < ROWS)
    {
        big += " " + little +"\n";
        count ++;
    }
    else
        big = " "; // <----  qui ci puo´ essere un problema di perdita di
memoria?
    // questa funzione mostra sul video la stringa
    #ifdef DEBUG
    show_on_the screen(big);
    #endif
}
int main()
{
    int i;
    string s0 = "Ciao",  s1 = "a tutti.", s2 = "Questa e´", s3 = "una
prova";
    for (i = 0; i < 100; i++)
    {
        add_string(s0);
        add_string(s1);
        add_string(s2);
        add_string(s3);
    }
    return 0;
}
La mia paura e´ che alla riga che ho commentato con <---- nella quale big
prende il valore " ", ci sia qualche memory leak.
La grossa stringa che era in big, che fine fa visto che non l´ho allocata
dinamicamente e quindi non posso parne una delete?
Altrimenti come potrei fare la stessa cosa in maniera dinamica?
Mauro

applicazione).
Che compilatore stai usando? Con VC++ ti segnala un memory-leak su
tutte le 'string' dichiarate... ma in realtà è un falso errore. Vai
tranquillo.
Luc.

std::string usa memoria allocata dinamicamente, ma la gestisce
completamente al suo interno. Quindi, a meno di un'implementazione
gravemente bacata, non ci sono problemi di memory leak. Non e' detto
pero' che il fatto di assegnare una stringa piu' piccola comporti
un'effettiva riduzione dello spazio occupato, perche' potrebbe rimanere
riservato per anticipare estensioni (della stringa) future.
Ciao,
Nicola

        big = " "; // <----  qui ci puo´ essere un problema di perdita di
memoria?
ma in realta´ in quella riga faccio:
big = little;
ma penso che il concetto sia lo stesso.
Ovviamente ho dimenticato di scrivere:
using namespace std;
altrimenti il compilatore da´ errore.
Sto usando il VC+ 6 e non e´ lui che mi da´ il memory leak ma Windows CE
dove faccio girare la mia applicazione, quando attivo questa funzione, apro
una finestra dove viene mostrata la memoria, che man mano viene mangiata
dalla mia applicazione.
Ho paura che quando faccio l´assegnamento "big = little;" tutta la memoria
che c´era nella stringa big non venga riallocata.
Mauro

Altrimenti come potrei fare la stessa cosa in maniera dinamica?
Non devi seguire questa scelta, a mio avviso, se significa rinunciare
agli oggetti.
--
Andrea Laforgia (ICQ 87230455)
http://www.geocities.com/AndreaLaforgia
"ci sono tre tipi di persone nel mondo : quelli che
 sono bravi in matematica e quelli che non lo sono."

 che c´era nella stringa big non venga riallocata.
Primo non dovrebbe essere riallocata ma al massimo liberata. Secondo
se verifichi la cosa in quel modo ti perdi per una serie infinita di
motivi:
1) non e' garantito che la memoria venga liberata all'atto
dell'assegnamento, ma e' garantito che venga liberata all'atto della
distruzione della stringa.
2) se anche l'oggetto stringa libera la memoria (con delete) non e'
detto che il sistema di allocasione (new/delete o anche malloc/free)
la liberi: potrebbe benissimo "considrarla libera" ma tenerla allocata
al programma e riutilizzarla ad una successiva riallocazione. La tua
"finestra" che monitorizza l'uso della memoria da parte
dell'applicazione non se ne accorgerebbe.
3) se anche la memoria venisse "restituita" al sistema operativo, non
e' detto che il sistema operativo a sua volta la renda libera:
potrebbe benissimo "considrarla libera" ma tenerla allocata al
programma e riutilizzarla ad una successiva riallocazione. La tua
"finestra" che monitorizza l'uso della memoria da parte
dell'applicazione non se ne accorgerebbe.
Se: l'istanza di string decide di liberare la memoria e la restituisce
all'allocator, l'allocator decide di non riutilizzare la memoria e di
restituirla al sistema di allocazione dinamica, il sistema di
allocazione dinamica decide di non riutilizzare la memoria e di
restituirla al sistema operativo, il sistema operativo decide di non
riutilizzare la memoria e di restituirla al pool di memoria libra di
sistema. Solo se tutto questo accade, tu vedi la quantita' di memoria
usata dalla tua applicazione diminuire!
Andrea Ferro
---------
Brainbench C++ Master. Scored higher than 97% of previous takers
Scores: Overall 4.46, Conceptual 5.0, Problem-Solving 5.0
More info 
http://www.brainbench.com/transcript.jsp?pid=2522556

useresti in Basic ;)
Hmmm... condivido in parte. Innanzitutto e' lecito (secondo me
addirittura doveroso) domandarsi come le cose funzionino dietro
le quinte. Se non ti fai queste domande prima o poi userai gli
oggetti forniti molto a sproposito (con prestazioni indecenti)
o addirittura in maniera errata (es. ti capitera' di invalidare
degli iteratori senza accorgetene).
In secondo luogo il dire "presumibilmente corretto" e' secondo me
il giusto atteggiamento mentale, ma altrettanto giusto e'
controllare che il problema visualizzato nel caso specifico
(WinCE) non sia *effettivamente* un memory leak.
Un cliente con un programma che non funziona (o che si pianta
dopo un po' che funziona) non credo che sia interessato al fatto
che il bug sia tuo oppure delle librerie di sistema. Quali
librerie usare lo hai scelto tu... se toppano e' *comunque*
un problema tuo.
Purtroppo esistono a dire il vero anche contesti in cui
l'atteggiamento prevalente non e' cercare di far funzionare
le cose ma semplicemente il cercare di non essere accusati di
essere i responsabili... ma questo e' un altro discorso.
Andrea
--
Andrea Griffini, Programmer
http://www.netservers.com/~agriff

un problema tuo.
Accidenti!! Purtroppo nel mondo dell'informatica di librerie e
pacchetti di sviluppo "professionali" buggati ne esistono fin troppi,
ed é proprio per questo che io sarei ben più felice di scrivermi tutto
il mio programma da solo senza affidarmi a porcherie scritte da altri
e pronte per l'uso(o pronte per il cestino).Magari questo vuol dire
decuplicare i tempi di sviluppo e sbattere la testa su un sacco di
problemi, ma se non altro sarebbe garantito il pieno controllo del
funzionamento del programma da parte di chi lo ha sviluppato.
Tuttavia ritengo la libreria standard del c++ sufficiente integrata
nel linguaggio e abbastanza affidabile(anche se in alcuni casi dubito
fortemente sulle prestazioni che é possibile ottenere)da non dover
costringere a reinventare ogni volta la ruota.
Il Ghenio
WINWORD.EXE - ERRORE DI APPLICAZIONE
l'istruzione a "0x3017ff27"ha fatto riferimento alla
memoria a "0x02a3143c".La memoria non poteva essere 'read'.

 costringere a reinventare ogni volta la ruota.
Teoria. Solo teoria. Non esiste ancora una implementazione della STL
totalmente conforme. Anche perche' non esiste una implementazione del
linguaggio che sia totalmente conforme. Quindi e' difficilotto
implementare una libreria che funziona su un compilatore che non
funziona.
Viceversa esistono molte ottime implementazioni della STL che
"spremono" i compilatori attuali. Ovvero sono MOLTO ben ottimizzate.
Ovviamente sottostanno alle limitazioni del compilatore e del sistema
operativo. Ma questa e' un'altra questione.
Invece che reinventare la ruota il mio consiglio e' di usare la
libreria (salvo quando e' bacata) e semmai di spendere il tempo nello
scegliere una buona implementazione.
Tu quale usi per dire che e' abbastanza affidabile?


 o addirittura in maniera errata 
...
oh, questo commento mi piace molto. Quindi perché in questo ng quando la 
gente vuole saperne di più sui vari meccanismi gli si risponde 
immancabilmente di utilizzare la libreria standard? Non è un po' in 
contrasto?
p.s. non mi sto riferendo esplicitamente a te, anché perché non conservo 
statistiche sulle risposte date a domande di questo genere.

(WinCE) non sia *effettivamente* un memory leak.
E se fosse un memory leak dovuto a WinCE, che c'entra la classe ?


funzionamento del programma da parte di chi lo ha sviluppato.
Secondo me non e' garantito un bel niente.
Anzi, a mio avviso, tanto piu' si rifa' qualcosa, tanto piu' c'e' la
possibilita' di cadere in errore.
Se poi avete la facolta' di rispondere a chi vi chiede "quanto ti ci
vuole per ultimare il programma ?" con "sa, mi serve dieci volte il
tempo previsto, perche' devo riscrivermi tutta la libreria", be',
allora siete fortunati davvero ;) 


contrasto?
No.
La mia risposta e' : perche' quello e' l'atteggiamento migliore.
Le classi della libreria standard sono state inventate per essere
usate e poi, se se ne ha il tempo, si puo' capire cosa avviene
internamente.
Non e' giusto rispondere a qualcuno che vuole utilizzare un vettore
con "scriviti una classe vettore, cosi' capirai cosa avviene
internamente alla classe standard std::vector", ma gli si dice di
utilizzare quest'ultima, perche' questo e' il modo corretto di
programmare. Poi, nessuno sta negando la possibilita' di studiare il
codice sorgente, anzi, lo studio dei sorgenti di *qualsiasi* libreria
e' consigliabile (ma, appunto, se ne deve avere il tempo).


e' sicuro che potrei sbagliarmi anch'io :)
Sono in gran parte daccordo con te. Però metto un eccezione quando si
ricerca la prestazione. (in questo caso le limitazioni di windows Ce
possono essere determinanti).
Queste librerie però in genere sono pensate per essere generiche e
adatte a tutti gli usi. Quindi rispetto al problema che si deve
risolvere sono spesso sovrabbondanti e poco prestanti sia in termini
di velocità che di spazio. Inoltre più aumenta il codice maggiore è la
probabilità di errore.
Ho esperienza di un programma dove utilizzando 
stringa1=stringa2+stringa3 in un ciclo molto lungo(processavo un file
di testo di alcuni giga) ad un certo punto il programma andava in
crash.
ho sostituito (senza problemi perchè tutte le dimensioni massime delle
stringhe mi erano note) la riga di programma incriminato con un
strcat() è tutto è andato a posto.
Per le assegnazioni fuori dal ciclo ho continuato a utilizzare la
classe (probabilmente bacata) senza problemi.
Posto anche la classe ( non di mia creazione ) non si sa mai che
qualcuno riesca a capire dove sbaglia io non ho trovato nessun errore.
--------------------File str.h--------------------------------
#if !defined(__STR_H__)
#define __STR_H__
#include <string.h>
class CStr
{
public:
	// constructors and destructor
	CStr(const CStr& str);
	CStr(const char* str);
	CStr(const double var)				{
VarToString(var); }
	CStr()
{ m_nLength = 0; m_pString = 0; }
	virtual ~CStr()						{ if
(m_pString) delete m_pString; }
	// operator overloading helper
	template <class T> friend CStr _cdecl operator +(T var, const
CStr& str);
	// operator overloading
	CStr& operator  =(const char* str);
	CStr& operator  =(const CStr& str);
	CStr& operator  =(const double var)	{ VarToString(var);
return *this; }
	template <class T>
	CStr  operator  +(T var)			{ CStr tstr =
*this; return tstr += var; }
	CStr& operator +=(double str)		{ return *this +=
(CStr)str; }
	CStr& operator +=(const char* str)	{ return *this +=
(CStr)str; }
	CStr& operator +=(const CStr& str);
	// add more logic comparison operators as following, for
example, although not efficient
	virtual bool operator !=(char* str)	{ return strcmp(str,
m_pString) != 0; }
	// c type string conversion
	operator char* ()					{
return m_pString; }
	operator const char* ()	const		{ return m_pString; }
	char* GetChar()						{
return m_pString; }
	// numeric conversion
	template <class T> GetValue(T& var)	{ return GetVar(var);
}
	// search the match string : WildCards can be '?' and '*'
combination
	// return value : true (pattern matchs string), false (no
match)
	bool Search(const char* WildCards)	{ return
Match((char*)WildCards, m_pString); }
	// format string
	int Format(const char* format, ...);
protected:
	// can use faster algorithm for search ?
	virtual bool Match(char*, char*);
	virtual bool Scan(char*&, char*&);
	// have any good conversion method ?
	virtual void VarToString(const double var);
	// numeric conversion helpers
	bool NumericParse(void* pvar, char flag);
	bool GetVar(bool& var)				{ return
NumericParse((void*)&var, 'b'); }
	bool GetVar(char& var)				{ return
NumericParse((void*)&var, 'c'); }
	bool GetVar(short& var)				{ return
NumericParse((void*)&var, 's'); }
	bool GetVar(int& var)				{ return
NumericParse((void*)&var, 'i'); }
	bool GetVar(long& var)				{ return
NumericParse((void*)&var, 'l'); }
	bool GetVar(float& var)				{ return
NumericParse((void*)&var, 'f'); }
	bool GetVar(double& var)			{ return
NumericParse((void*)&var, 'd'); }
	bool GetVar(unsigned char& var)		{ return
NumericParse((void*)&var, 'C'); }
	bool GetVar(unsigned short& var)	{ return
NumericParse((void*)&var, 'S'); }
	bool GetVar(unsigned int& var)		{ return
NumericParse((void*)&var, 'I'); }
	bool GetVar(unsigned long& var)		{ return
NumericParse((void*)&var, 'L'); }
	// data block
	int   m_nLength;
	char* m_pString;
};
template <class T>
CStr operator +(T var, const CStr& str) 
{ 
	CStr svar = var;
	return svar += str; 
}
#endif
----------------------File str.cpp -----------------------------
#include <stdio.h>
#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include "Str.h"
CStr::CStr(const char* str)
{
	if(str == 0)
	{
		m_nLength = 0;
		m_pString = 0;
	}
	else
	{
		m_nLength = strlen(str);
		m_pString = new char[m_nLength + 1];
		assert(m_pString != 0);
		strcpy(m_pString, str);
	}
}
CStr::CStr(const CStr& str)
{ 
	if(str == 0)
	{
		m_nLength = 0;
		m_pString = 0;
	}
	else
	{
		m_nLength = str.m_nLength; 
		m_pString = new char[m_nLength + 1];
		assert(m_pString != 0);
		strcpy(m_pString, str.m_pString); 
	}
}
CStr& CStr::operator +=(const CStr& str)
{
	m_nLength += str.m_nLength;
	char* pNew = new char[m_nLength + 1];
	assert(pNew != 0);
	strcpy(pNew, m_pString);
	strcat(pNew, str.m_pString);
	delete m_pString;
	m_pString = pNew;
	return *this;
}
CStr& CStr::operator =(const char* str)
{
	if (m_pString) delete m_pString;
	m_nLength = strlen(str);
	m_pString = new char[m_nLength + 1]; 
	assert(m_pString != 0);
	strcpy(m_pString, str);
	return *this;
}
CStr& CStr::operator =(const CStr& str)
{
	assert(str != 0);
	if (m_pString) delete m_pString;
	m_nLength = strlen(str.m_pString);
	m_pString = new char[m_nLength + 1]; 
	assert(m_pString != 0);
	strcpy(m_pString, str.m_pString);
	return *this;
}
void CStr::VarToString(const double var)
{
	char str[32];
	gcvt(var, 16, str);
	m_nLength = strlen(str);
	if (str[m_nLength - 1] == '.')
	{
		str[m_nLength - 1] = '\0';
		m_nLength --;
	}
	m_nLength = strlen(str);
	m_pString = new char[m_nLength + 1];
	assert(m_pString != 0);
	strcpy(m_pString, str);
}
int CStr::Format(const char* format, ...)
{
	assert(format != 0);
	int len;
	char* MaxBuf;
	for(int i = 5; ; i ++)
	{
		len = (int)pow(2, i);
		MaxBuf = new char[len];
		if (!MaxBuf) return 0;
		// some UNIX's do not support vsnprintf and snprintf
		len = _vsnprintf(MaxBuf, len, format, (char*)(&format
+ 1));
		if (len > 0) break;
		delete []MaxBuf;
		if (len == 0) return 0;
	}
	if (!m_pString)
	{
		m_nLength = len;
		m_pString = new char[m_nLength + 1];
	}
	else if (m_nLength < len)
	{
		delete m_pString;
		m_nLength = len;
		m_pString = new char[m_nLength + 1];
	}
	if (m_pString) 
		strcpy(m_pString, MaxBuf);
	else
		len = 0;
	delete []MaxBuf;
	return len;
}
bool CStr::Match(char* Wildcards, char* str)
{
	bool Yes = 1;
	//iterate and delete '?' and '*' one by one
	while(*Wildcards != '\0' && Yes && *str != '\0')
	{
		if (*Wildcards == '?') str ++;
		else if (*Wildcards == '*')
		{
			Yes = Scan(Wildcards, str);
			Wildcards --;
		}
		else
		{
			Yes = (*Wildcards == *str);
			str ++;
		}
		Wildcards ++;
	}
	while (*Wildcards == '*' && Yes)  Wildcards ++;
	return Yes && *str == '\0' && *Wildcards == '\0';
}
// scan '?' and '*'
bool CStr::Scan(char*& Wildcards, char*& str)
{
	// remove the '?' and '*'
	for(Wildcards ++; *str != '\0' && (*Wildcards == '?' ||
*Wildcards == '*'); Wildcards ++)
		if (*Wildcards == '?') str ++;
	while ( *Wildcards == '*') Wildcards ++;
 
	// if str is empty and Wildcards has more characters or,
	// Wildcards is empty, return 
	if (*str == '\0' && *Wildcards != '\0') return false;
	if (*str == '\0' && *Wildcards == '\0')	return true; 
	// else search substring
	else
	{
		char* wdsCopy = Wildcards;
		char* strCopy = str;
		bool  Yes     = 1;
		do 
		{
			if (!Match(Wildcards, str))	strCopy ++;
			Wildcards = wdsCopy;
			str		  = strCopy;
			while ((*Wildcards != *str) && (*str != '\0'))
str ++;
			wdsCopy = Wildcards;
			strCopy = str;
		}while ((*str != '\0') ? !Match(Wildcards, str) : (Yes
= false) != false);
		if (*str == '\0' && *Wildcards == '\0')	return true;
		return Yes;
	}
}
bool CStr::NumericParse(void* pvar, char flag)
{
	assert(m_pString != 0);
	char* pTmpStr = m_pString;
	// remove the leading ' ' and '\t' at the beginning
	while (*pTmpStr == ' ' || *pTmpStr == '\t')
		pTmpStr++;
	// no desired character found
	if (strlen(pTmpStr) == 0)
		return false;
	char a = pTmpStr[0];
	if ((flag == 'b' || flag == 'C' || flag == 'S' || 
		flag == 'I' || flag == 'L') && a == '-')
		return false;
	if (flag == 'b')
	{
		bool var;
		if (strcmp(pTmpStr, "true") == 0 || strcmp(pTmpStr,
"1") == 0 ||
			strcmp(pTmpStr, "TRUE") == 0) var = true;
		else if (strcmp(pTmpStr, "false") == 0 ||
strcmp(pTmpStr, "0") == 0 ||
			strcmp(pTmpStr, "FALSE") == 0) var = false;
		else // failed
			return false;
		memcpy(pvar, &var, sizeof(bool));
		return true;
	}
	else
	{
		double tmpvar = strtod(pTmpStr, (char**)&pTmpStr);
		if (tmpvar == 0.0 && a != '0')
			return false;   // convertion wrong
		if (flag == 'f' || flag == 'd')
		{
			// allow any float value with one 'f' or 'F'
terminated
			if (*pTmpStr == 'f' || *pTmpStr == 'F') 
				pTmpStr++;
		}
		else if (flag == 'l' || flag == 'L')
		{
			// allow any float value with one 'l' or 'L
terminated
			if (*pTmpStr == 'l' || *pTmpStr == 'L') 
				pTmpStr++;
		}
		switch(flag)
		{
		case 'c':
			{
				//if (tmpvar < -(0xff / 2 + 1) ||
tmpvar > 0xff / 2)
				if (tmpvar < -128 || tmpvar > 127)
					return false;   // onerflow
				char var = (char)tmpvar;
				memcpy(pvar, &var, sizeof(char));
			}
			break;
		case 's':
			{
				//if (tmpvar < -(0xffff / 2 + 1) ||
tmpvar > 0xffff / 2)
				if (tmpvar < -32768.0 || tmpvar >
32768.0)
					return false;   // onerflow
				short var = (short)tmpvar;
				memcpy(pvar, &var, sizeof(short));
			}
			break;
		case 'i':
			{
				//if (tmpvar < -(0xffffffff / 2 + 1)
|| tmpvar > 0xffffffff / 2)
				if (tmpvar < -
2147483648
.0 || tmpvar >
2147483647
.0)
					return false;   // onerflow
				int var = (int)tmpvar;
				memcpy(pvar, &var, sizeof(int));
			}
			break;
		case 'l':
			{
				//if (tmpvar < -(0xffffffff / 2 + 1)
|| tmpvar > 0xffffffff / 2)
				if (tmpvar < -
2147483648
.0 || tmpvar >
2147483647
.0)
					return false;   // onerflow
				long var = (long)tmpvar;
				memcpy(pvar, &var, sizeof(long));
			}
			break;
		case 'C':
			{
				//if (tmpvar < 0 || tmpvar > 0xff)
				if (tmpvar < 0.0 || tmpvar > 255)
					return false;   // onerflow
				unsigned char var = (unsigned
char)tmpvar;
				memcpy(pvar, &var, sizeof(unsigned
char));
			}
			break;
		case 'S':
			{
				//if (tmpvar < 0 || tmpvar > 0xffff)
				if (tmpvar < 0.0 || tmpvar > 65535.0)
					return false;   // onerflow
				unsigned short var = (unsigned
short)tmpvar;
				memcpy(pvar, &var, sizeof(unsigned
short));
			}
			break;
		case 'I':
			{
				//if (tmpvar < 0 || tmpvar >
0xffffffff)
				if (tmpvar < 0.0 || tmpvar >
4294967295.0)
					return false;   // onerflow
				unsigned int var = (unsigned
int)tmpvar;
				memcpy(pvar, &var, sizeof(unsigned
int));
			}
			break;
		case 'L':
			{
				//if (tmpvar < 0 || tmpvar >
0xffffffff)
				if (tmpvar < 0.0 || tmpvar >
4294967295.0)
					return false;   // onerflow
				unsigned long var = (unsigned
long)tmpvar;
				memcpy(pvar, &var, sizeof(unsigned
long));
			}
			break;
		case 'f':
			{
				if (tmpvar < -3.402823466e+38 ||
tmpvar > 3.402823466e+38)
					return false;   // onerflow
				float var = (float)tmpvar;
				memcpy(pvar, &var, sizeof(float));
			}
			break;
		case 'd':
			memcpy(pvar, &tmpvar, sizeof(double));
			break;
		}
		// remove the leading ' ' and '\t' at the end
		while (*pTmpStr == ' ' || *pTmpStr == '\t')
			pTmpStr++;
		if (*pTmpStr != '\0')
			return false;   // non digital character
detected
		return true;
	}
}

qualcuno riesca a capire dove sbaglia io non ho trovato nessun errore.
Spero tu ti renda conto che hai postato ben 515 righe di codice.
Non e' una buona regola farlo, specie quando non viene richiesto.
Su Usenet, la regola e' quella di postare il codice di dimensioni
minime, che esponga il problema in esame.


 --
il problema era esattamente opposto!
il fatto è che a volte vengono chieste informazioni su meccanismi di 
basso livello per ottenere come risposta l'invito a utilizzare la 
libreria standard. E' questo che non è tanto corretto, perché un conto è 
chiedere come si fa a ridimensionare un array (domanda non a 
basso livello, ma indicativamente dovuta alla necessita di risolvere un 
problema puntuale - giustamente gli si deve rispondere di usare vector e 
il suo metodo appropriato), un conto è chiedere quali sono i passi 
necessari per effettuare il ridimensionamento! se uno lo sa, utilizza 
meglio vector, list, map, set etc etc. giusto? Non dico mica che ognuno 
deve riscriversele, per carità!

qualcuno riesca a capire dove sbaglia io non ho trovato nessun errore.
Ho dato una rapida occhiata e mi sembra ci siano molti errori e
imprecisioni: i piu' gravi sono
 - La memoria viene allocata con char "*p=new char[len]" e
   viene liberata con "delete p" quando invece si deve
   usare "delete[] p".
 - Non c'e' una coerenza di gestione della stringa nulla;
   in alcuni casi e' ammesso passare un char * nullo (es.
   costruttore) e in altri casi la cosa e' vietata con una
   assert (es. assegnazione)
 - Non viene gestita correttamente la stringa nulla e si
   chiamano funzioni come strcpy o strlen passando puntatori
   nulli (cosa non permessa). Ad esempio concatenare con
   stringhe nulle crea problemi.
Per chi e' interessato ho reso disponibile una versione
commentata:
http://www.netservers.com/~agriff/download.cgi/strcomm.zip
Si tratta di commenti scritti dopo una semplice lettura
veloce (non ho nemmeno provato a compilare) quindi vanno
presi con cautela. Personalmente questa classe CStr mi
sembra abbastanza discutibile anche come stile.
HTH


class CStr
perché non hai usato std::string? è sicuramente più testata e ci sono
meno probabilità che sia bacata.
Anto
--
Antonio Cuni, 18 anni, studente
Brainbench Certification: 
Master C++: 4.44, higher than 97% of all previous test takers.
Visual Basic 5: 3.06, higher than 77% of all previous test takers.
http://www.brainbench.com/transcript.jsp?pid=1301688