Newsgroup: it.comp.lang.c++
Titolo: Scusate se rompo... (gli iteratori ed io)

Rieccomi. Nuovamente senza alcuna voglia di imparare seriamente il C++, 
nuovamente nella mota fino al collo per questo. Per farla breve: questa 
volta si tratta di una bella struttura ad albero stile directory che ho 
deciso di gerarchizzare con il potere della map:
class CNode
{
 public:
  bool m_IsDir;
  CNode * m_Parent;
  CNode
  (
   _blah_t * _Source,
   bool _IsDir,
   CNode * _Parent
  ): m_IsDir(_IsDir), m_Parent(_Parent)
  {
  }
}
class CLeaf: public CNode
{
 public:
  CLeaf
  (
   _blah_t * _Source,
   CNode * _Parent
  ): CNode(_Source, false, _Parent){}
}
class CContainer: public CNode
{
 public:
  map<unsigned long, CNode *> m_Children;
  CContainer
  (
   _blah_t * _Source,
   CNode * _Parent
  ): CNode(_Source, true, _Parent)
  {
   // enumera i membri di _Source. Per ognuno, crea un CContainer o una
   // CLeaf a seconda di una certa flag, e mappalo in m_Children con
   // l'hash dell'identificatore
  }
}
Ora, a parte il non indifferente sbattimento ad imparare abbastanza C++ 
negli ultimi quattro giorni da mettere in piedi qualcosa di minimamente 
compilabile, qual č il problema che non riesco a risolvere? dovrei, nel 
distruttore di CContainer, iterarmi la mappa m_Children dei nodi figli, 
e seminare morte e distruzione. Ma come?
La guida di SGI dice che il concetto di iteratore č la generalizzazione 
del concetto di puntatore. Che avrei dovuto fare io? da brava testolina 
procedurale quale sono, l'ho trattato da puntatore:
~CContainer()
{
 map<unsigned long, CNode *>::iterator oCurChild = m_Children.begin();
 while(oCurChild)
 {
  // sģ, so che esiste static_cast, ma m_IsDir dovevo mettercelo
  // comunque, quindi ne approfitto
  if((*oCurChild)->m_IsDir)
   delete (CContainer *)(*oCurChild);
  else
   delete (CLeaf *)(*oCurChild);
  ++ oCurChild;
 }
}
che perņ non funziona perchč:
 - l'iteratore non definisce il cast a bool
 - dereferenziare l'iteratore non restitusce un map<blah>::value_type
Che faccio, mi sparo? ho gią provato a leggere la guida, ma a me sembra 
troppo superiore a queste banalitą per parlarne
-- 
 < 
http://www.reactos.com/
 >  Open source clone of Windows NT. Current
     Don't stand, REACT!      version 0.0.20. C, C++ and ASM developers
                              and beta testers are welcome!


Ti ho modificato il codice cosi' :
// disable warning C4786: symbol greater than 255 characters,
// okay to ignore
// Maurus: questo pragma e' per evitare dei warnings del VisualC++, noto bug
del compilatore
#pragma warning(disable: 4786)
#include <map>
typedef long _blah_t;
// Maurus: non so che tipo sia questo _blah_
// Maurus: per comodita' ho usato un long
class CNode
{
public:
 bool m_IsDir;
 CNode * m_Parent;
 CNode(_blah_t * _Source, bool _IsDir, CNode * _Parent) :
  m_IsDir(_IsDir), m_Parent(_Parent)
 {}
};
class CLeaf: public CNode
{
public:
  CLeaf(_blah_t * _Source, CNode * _Parent) :
   CNode(_Source, false, _Parent){}
};
class CContainer: public CNode
{
public:
// Maurus: tutti i tuoi map devono essere preceduti da std::
 std::map<unsigned long, CNode *> m_Children;
 CContainer(_blah_t * _Source, CNode * _Parent) :
  CNode(_Source, true, _Parent)
 {
 // enumera i membri di _Source. Per ognuno, crea un CContainer o una
 // CLeaf a seconda di una certa flag, e mappalo in m_Children con
 // l'hash dell'identificatore
 }
 ~CContainer()
 {
  std::map<unsigned long, CNode *>::iterator oCurChild = m_Children.begin();
// Maurus: qua il controllo va fatto sull end()
// Maurus: usando il != e non il < che non e' definito
  while(oCurChild != m_Children.end())
  {
   // sě, so che esiste static_cast, ma m_IsDir dovevo mettercelo
   // comunque, quindi ne approfitto
// Maurus: devi deferenziare oCurChild per ottenere il map
// Maurus: e poi prendere il campo second del map che e' un
// Maurus: CNode * , poi puoi accedere a m_IsDir
   if(((*oCurChild).second)->m_IsDir)
    delete (CContainer *)((*oCurChild).second);
   else
    delete (CLeaf *)((*oCurChild).second);
   ++ oCurChild;
  }
 }
};
Nel costruttore di CContainer mi sembra di aver capito che tu dovrai
allocare dinamicamente oggetti di tipo CLeaf o di tipo CContainer.
Spero che sia tutto chiaro (anche da parte mia).
Maurus


Gravissima dimenticanza!
Poiche' la classe CNode e' una classe base da cui derivi CLeaf e CContainer,
devi anche dichiarare virtual il suo distruttore, altrimenti avrai guai in
fase di distruzione degli oggetti delle classi derivate:
 class CNode
{
public:
    bool m_IsDir;
    CNode * m_Parent;
    CNode(_blah_t * _Source, bool _IsDir, CNode * _Parent) :
    m_IsDir(_IsDir), m_Parent(_Parent)
    {}
    virtual ~CNode() {} // Non ti dimenticar di me!
ecc..ecc..
Maurus

 classi derivate:
odio dover mettere roba virtuale. Già così la distruzione dell'albero è 
lentissima (all'inizio, allocavo tutti i nodi in uno heap privato, così 
distruggendo l'heap deallocavo tutto in un colpo solo. Poi sono saltati 
fuori oggetti passati dall'esterno, e ho dovuto lasciar stare)
Ma perchè, poi? non è implicita la chiamata del distruttore? non faccio 
mai la delete di un CNode *, dopotutto


 CContainer. 
è-satto. Ora, se fossi così gentile da spiegarmi un'altra cosa... 
mappando un oggetto, voglio che non ci siano altri oggetti con lo 
stesso identificatore già presenti. Ho provato con:
// l'assert è un po' eccessivo, ma è solo temporaneo
assert(!m_Children.find(nIDHash));
ma pare che all'iteratore manchi l'operatore "!". Che fo?
PS: al C++ Builder non sembrano piacere molto gli oggetti anonimi 
allocati sullo stack. Arrrgh...


 allocati sullo stack. Arrrgh...
Per favore, mostra il codice che non "piace" al Builder.
Max

 è lentissima
Ti suggerirei di verificare, con l'ausilio di un profiler (o di un
cronometro), se effettivamente
   if( ptr->m_IsDir ) { delete /* cast ptr a CLeaf* */   }
   else {  delete /* cast ptr a CContainer* */   }
sia più efficiente di
   delete ptr; /* con il dtor di CNode dichiarato virtual */
Max

 }
Innanzitutto, dichiara 'virtual' il distruttore di CNode.
Quindi, nel distruttore di CContainer...
  map<unsigned long, CNode *>::iterator i = m_Children.begin();
  map<unsigned long, CNode *>::iterator end = m_Children.end();
  for( ; i != end ; ++i ) 
  { delete (*i).second;
  }
(Ovviamente, questo presuppone che nella map non ci sia piů di una volta
lo stesso puntatore.)
 
Max

 è lentissima
Ti suggerirei di verificare, con l'ausilio di un profiler (o di un


 cronometro), se effettivamente [...]
non importa, mi fido. E non è che la gerarchia debba essere ricostruita 
così spesso, dopotutto


 Per favore, mostra il codice che non "piace" al Builder.
Il mio codice, ridotto all'osso:
#include <windows.h>
#include <ole2.h>
#include <oleauto.h>
#import "msxml3.dll"
int main(void)
{
 ISequentialStreamPtr oInput;
 MSXML2::IXMLDOMDocumentPtr oDoc;
 oDoc->load(_variant_t((IUnknown *)oInput));
 return 0;
}
e la reazione del Builder:
D:\home\Juliet\desktop>bcc32 -q -w-inl -c doh.cpp
doh.cpp:
Assertion failed doh.cpp(11) : entryDestList == F.F_destructorList at 
c:\helena\bcc\indep\cppinl.c(2848)
Ho errori simili anche con il cast da VARIANT a _bstr_t. Cambiando il 
codice in:
#include <windows.h>
#include <ole2.h>
#include <oleauto.h>
#import "msxml3.dll"
int main(void)
{
 ISequentialStreamPtr oInput;
 MSXML2::IXMLDOMDocumentPtr oDoc;
 _variant_t vaInput = oInput;
 oDoc->load(vaInput);
 return 0;
}
funziona tutto. Un bug del compilatore direi. Forse gli chiedo troppo 
inlining


 inlining
dipende, quello che hai scritto nelle tue due versioni non è del tutto
equivalente. Innanzitutto la versione equivalente alla prima dovrebbe
essere questa:
_variant_t vaInput ((IUnknown *)oInput);
oDoc->load(vaInput);
ma non è quello che scrivi tu. Poi, nella dichiarazione del metodo load di
MSXML2::IXMLDOMDocumentPtr che parametro accetta:
_variant_t
_variant_t&
_variant_t const& (o const _variant_t&)
?
-- 
questo articolo e` stato inviato via web dal servizio gratuito 
http://www.newsland.it/news
 segnala gli abusi ad 
ab...@newsland.it

 load di MSXML2::IXMLDOMDocumentPtr che parametro accetta:
VARIANT. E _variant_t deriva da VARIANT


 dipende. Il costruttore che accetta un IUnknown * potrebbe anche
fare > la stessa cosa dell'operator=(IUnknown *), e mi pare la faccia
non credo che entrambe le versioni facciano la stessa cosa, e il
motivo di questo mio dubbio è facilmente verificabile: una versione
funziona, l'altra no, quindi da qualche parte una differenza c'è.
In ogni caso, quello che vedi tu non è un errore in fase di
compilazione, ma un assert che fallisce (a compile-time, strano). In
ogni caso il bug del compilatore è l'ultima cosa a cui dovresti
pensare.