PDA

Visualizza la versione completa : [C++] Mappa e pointer of pointer


giuseppe500
07-12-2009, 15:29
ciao.
Ho provato a semplificare il sistema , ho aperto un altro post che è un po confusionario , quindi pregerei il moderatore se ci sono problemi di chiudere l'altro post , non questo.
posto il codice:




#include "stdafx.h"
#include "A.h"
#include "B.h"
#include <string>
using namespace std;
main
[code]
int _tmain(int argc, _TCHAR* argv[])
{
CA *c =new CA();
c->processA();
CB* data = c->GetCB();
CC** cx = data->m_mappa[0];

CC* ce = * cx;
wstring str = ce->GetName();

ce = * cx;
str = ce->GetName();


return 0;
}

A.h


#pragma once
#include "B.h"
class CA
{
public:
CA(void);
~CA(void);
void processA();
CB* GetCB();
private:
CB *m_b;
};

A.cpp


#include "StdAfx.h"
#include ".\a.h"

CA::CA(void)
{
}

CA::~CA(void)
{
}

void CA::processA()
{
CC* c= new CC();
CC* cx= new CC();
c->SetName(L"giugio");
cx->SetName(L"pippo");
m_b = new CB();
m_b->processaB(&c);
m_b->processaB(&cx);
}

CB* CA::GetCB()
{
return m_b;

}

B.h


#pragma once
#include "C.h"
#include <map>

using namespace std;

class CB
{
public:
CB(void);
~CB(void);
void processaB(CC ** pPointer);
map<int, CC**>m_mappa;
};

B.cpp


#include "StdAfx.h"
#include ".\b.h"

CB::CB(void)
{
}

CB::~CB(void)
{
}

void CB::processaB(CC** pC)
{
m_mappa[m_mappa.size()] = pC;

}

C.h


#pragma once
#include <string>

using namespace std;
class CC
{
public:
CC(void);
~CC(void);
void SetName(wstring str);
wstring GetName();
private:
wstring m_name;
};

C.cpp


#include "StdAfx.h"
#include ".\c.h"

CC::CC(void)
{
}

CC::~CC(void)
{
}

wstring CC::GetName()
{
return m_name;
}

void CC::SetName(wstring str)
{
m_name = str;
}



dove sono gli errori?
perchè nel main CC* ce = * cx; ce ha un puntatore a schifo(0Xccccccc)?e non il valore che gli ho assegnato?

Grazie

shodan
07-12-2009, 16:27
Perché come ti ho detto nell'altro post, stai assegnando indirizzi di varabili locali che saranno distrutte all'uscita della funzione.


CC* c= new CC(); // qui
CC* cx= new CC(); // e qui.
c->SetName(L"giugio");
cx->SetName(L"pippo");
m_b = new CB();
m_b->processaB(&c); // qui
m_b->processaB(&cx); // e qui


Devi usare variabili che persistano all'uscita della funzione.


CC** pc = new CC*;
CC** pcx = new CC*;
*pc= new CC();
*pcx= new CC();

c->SetName(L"giugio");
cx->SetName(L"pippo");
m_b = new CB();
m_b->processaB(pc);
m_b->processaB(pcx);

giuseppe500
07-12-2009, 16:32
il problema potrebbe essere qui?
[code]
CC* c= new CC();
CC* cx= new CC();
c->SetName(L"giugio");
cx->SetName(L"pippo");
m_b = new CB();
m_b->processaB(&c);
m_b->processaB(&cx);
[code]
anche se ho creato le variabili c e cx nello heap , poi le passo nello stack che in qualche modo me le distrugge?

shodan
07-12-2009, 17:24
Non fare confusione. Nell'heap hai creato zone di memoria a cui puntano poi le variabili locali.
Quando la funzione finisce, queste variabili locali sono distrutte.
Per renderle persistenti devi creare nell'heap anche quelle variabili, che è quello che succede con le modifiche che ho fatto.


CC** pc = new CC*; // creo una variabile puntatore, puntata da pc;
*pc = new CC; // creo una variabile CC puntata da *pc (che è un puntatore);

m_b->processaB(pc); // assegno pc a qualcosa. All'uscita della funzione pc è distrutto, ma la memoria a cui punta sopravvive.



Per inciso è lo stesso meccanismo usato per le matrici dinamiche. Poi devi pensare a come liberare la memoria.

giuseppe500
13-03-2010, 03:35
penso di aver capito shodan,ma adesso viene il tuo secondo punto:


Poi devi pensare a come liberare la memoria.


come si libera in genere la memoria in questi casi ?
quando eliminare il doppio puntatore?
E' possibile creare uno smartpointer di smartpointer per liberare automaticamente le risorse?
Grazie.
ciao.

shodan
13-03-2010, 13:03
In senso inverso. Prima si libera il puntatore, poi il puntatore a puntatore.
Il discorso "smartpointer di smartpointer" non ha senso. E come dire: una porta che chiude una porta.
Se fai fatica a comprendere gli smart pointer, rifletti sulla classe string e wstring. Se ci pensi bene anche loro sono smart pointer di un dato char* sottostante, eppure le usi senza preoccuparti di come è gestita la memoria di quel char*.
Lo smart pointer (a semantica condivisa) si comporta allo stesso modo: gli si affida la memoria senza preoccuparsi di come e quando è liberata.
Ho riscritto il tuo esempio usando gli shared_ptr (che saranno inclusi nel prossimo standard, ma di cui molti compilatori hanno l'impementazione anche adesso. L'unica differenza è nel namespace che qui ho trascurato, ma è ininfluente ai fini del discorso).


#include "stdafx.h"
#include "A.h"
#include "B.h"
#include <string>
#include <header_shared_ptr>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
shared_ptr<CA> c(new CA());
c->processA();
shared_ptr<CB> data = c->GetCB();
shared_ptr<CC> cx = data->m_mappa[0];

wstring str = cx->GetName();
cx = data->m_mappa[1];
str = cx->GetName();
return 0;
}



#pragma once
#include "B.h"
class CA
{
public:
CA(void);
~CA(void);
void processA();
shared_ptr<CB> GetCB();
private:
shared_ptr<CB> m_b;
};

#include "StdAfx.h"
#include ".\a.h"

CA::CA(void)
{
}

CA::~CA(void)
{
}

void CA::processA()
{
shared_ptr<CC> c(new CC());
shared_ptr<CC> cx(new CC());
c->SetName(L"giugio");
cx->SetName(L"pippo");
m_b.reset(new CB());
m_b->processaB(c);
m_b->processaB(cx);
}

shared_ptr<CB> CA::GetCB()
{
return m_b;
}



#pragma once
#include "C.h"
#include <map>

using namespace std;

class CB
{
public:
CB(void);
~CB(void);
void processaB(const shared_ptr<CC>& pPointer);
map<int, shared_ptr<CC> >m_mappa;
};

#include "StdAfx.h"
#include ".\b.h"

CB::CB(void)
{
}

CB::~CB(void)
{
}

void CB::processaB(const shared_ptr<CC>& pC)
{
m_mappa[m_mappa.size()] = pC;

}



#pragma once
#include <string>

using namespace std;
class CC
{
public:
CC(void);
~CC(void);
void SetName(const wstring& str);
wstring GetName() const;
private:
wstring m_name;
};

#include "StdAfx.h"
#include ".\c.h"

CC::CC(void)
{
}

CC::~CC(void)
{
}

wstring CC::GetName() const
{
return m_name;
}

void CC::SetName(const wstring& str)
{
m_name = str;
}

giuseppe500
13-03-2010, 15:05
grazie 1000 shodan.
Solo non ho capito come si relizzano i puntatori di puntatori con gli smart pointer .
Nel tuo esempio non ho trovato come è tradotto:

CC** pc = new CC*;
CC** pcx = new CC*;
*pc= new CC();
*pcx= new CC();

da quello che ho capito CC** anche se è un doppio puntatore è sempre un puntatore(e quindi uno smart pointer).
Adesso capisco la fesseria che ho detto di smartpointer di smartpointer, uno smartpointer tratta puntatori non puntatori di classe smartpointer.

Per sicurezza mi pui spiegare l'ultima cosa?
è qui il puntatore di puntatore?



shared_ptr<CA> c(new CA());


shared pointer di classe CA a cui viene assegnato un puntatore a CA.
Ma non un puntatore di puntatore CA.
Come mai?

in pratica come realizzare
CC** pc = new CC*;
con gli smartpointer perchè non è cosi?
shared_ptr<CC> c(new CC*())

grazie.

shodan
13-03-2010, 16:23
Nel codice che ho riscritto non ci sono puntatori di puntatori.
Il codice:


CC** pc = new CC*;
CC** pcx = new CC*;
*pc= new CC();
*pcx= new CC();

nasceva da una precisa esigenza:


stai assegnando indirizzi di variabili locali che saranno distrutte all'uscita della funzione.

per cui avevi aperto il thread.


Adesso capisco la fesseria che ho detto di smartpointer di smartpointer, uno smartpointer tratta puntatori, non puntatori di classe smartpointer.

Uno shared_ptr tratta puntatori di qualsiasi tipo, siano dati utente, siano altri shared_ptr. Quindi si può scrivere tranquillamente:


shared_ptr< shared_ptr <CA> > c;
// o
shared_ptr<CA*> c;

ma dopo si diventa matti per usarli.

Però che senso ha un puntatore "intelligente" che gestisce il lifetime di un altro puntatore "intelligente"? Non è più semplice usare un solo puntatore "intelligente"?

Onestamente, non capisco questa ossessione per i puntatori a puntatori.
Da quanto ne so, il loro impiego in C++ è abbastanza raro e sono usati solo per implementazioni didattiche di matrici dinamiche, o in qualche interfaccia per il C (o almeno io non ho trovato altri scopi al momento).

giuseppe500
13-03-2010, 16:36
grazie shodan.
Ti spiego la confusione che ho in testa sui puntatori a puntatore :
Io li uso se voglio in qualche modo "fare uscire" una variabile di cui è stato fatto il new all interno di un altro scope , o facendo il new al di fuori e poi popolando in un altra funzione per
questa era la tua spiegazione :



Perché come ti ho detto nell'altro post, stai assegnando indirizzi di varabili locali che saranno distrutte all'uscita della funzione.
codice:

CC* c= new CC(); // qui
CC* cx= new CC(); // e qui.
c->SetName(L"giugio");
cx->SetName(L"pippo");
m_b = new CB();
m_b->processaB(&c); // qui
m_b->processaB(&cx); // e qui



Devi usare variabili che persistano all'uscita della funzione.
codice:

CC** pc = new CC*;
CC** pcx = new CC*;
*pc= new CC();
*pcx= new CC();

c->SetName(L"giugio");
cx->SetName(L"pippo");
m_b = new CB();
m_b->processaB(pc);
m_b->processaB(pcx);



in particolare:
[quote]
Devi usare variabili che persistano all'uscita della funzione.
[quote]

da quello che ho capito devo usare puntatori sempre su di un livello dalla variabile che passi (dallo stak in pratica)se voglio che l'oggetto sia accessibile all' esterno della funzione.
sbaglio tutto?

shodan
13-03-2010, 18:13
Parti da questo assunto: una variabile posta sullo stack è distrutta al termine di una funzione.
Se vuoi che persistano devono risiedere nell'heap.


void CA::processA() {
CC* c= new CC(); // c è una variabile sullo stack.
CC* cx= new CC(); // cx è una variabile sullo stack..
c->SetName(L"giugio");
cx->SetName(L"pippo");
m_b = new CB();
m_b->processaB(&c);
// qui c esiste ancora.
m_b->processaB(&cx); // e qui
// qui cx esiste ancora.
}
// qui non esistono ne c ne cx.

processaB inserisce il puntatore a puntatore in una mappa, ma quando processA() termina, quello a cui puntano i puntatori a puntatori nella mappa diventa non valido; hai spazzatura e la memoria viene persa.
Per evitare questo, basta passare il puntatore; il puntatore a puntatore non serve a niente.


class CB {
public:
CB(void);
~CB(void);
void processaB(CC* pointer) {
m_mappa[m_mappa.size()] = pointer;
}
map<int,CC* >m_mappa;
};
...
void CA::processA() {
CC* c= new CC(); // c è una variabile sullo stack.
CC* cx= new CC(); // cx è una variabile sullo stack..
c->SetName(L"giugio");
cx->SetName(L"pippo");
m_b = new CB();
m_b->processaB(c);
// qui c esiste ancora.
m_b->processaB(cx); // e qui
// qui cx esiste ancora.
}
// qui non esistono ne c ne cx, ma ciò a cui puntavano sopravvive nella mappa di m_b

A questo punto posso anche dire: perché non usare un meccanismo che mi liberi da solo la memoria, indipendentemente da quante copie e/o assegnazioni faccio?
Ecco che usando lo shared_ptr ottengo quanto voglio ed è quello che ho scritto un paio di post fa.
E' un po' come avere un palloncino (la memoria) e un filo che lo tiene (il puntatore). Se non vuoi perdere il palloncino devi provvedere tu a legarlo con un nuovo filo (puntatore) prima che la forbice (l'uscita dalla funzione) tagli il filo vecchio. Se leghi il filo nuovo al filo vecchio (puntatore a puntatore), la forbice taglierà comunque il filo vecchio. Il risultato sarà che ti resterà in mano solo un inutile pezzo di filo, mentre il palloncino vola via. Ed è quello che succede nella prima versione.
Nella seconda versione invece hai il palloncino attaccato al filo (il puntatore c per esempio), prima che la forbice (l'uscita dalla funzione), leghi il palloncino a un secondo filo ( la variabile pointer in ProcessaB()) e poi a un terzo filo (in m_mappa). Tagli la variabile pointer, tagli c, ma il palloncino adesso è legato a m_mappa.
Usando uno shared_ptr hai la sicurezza che tutti i fili necessari verranno messi in automatico, senza che tu ti debba preoccupare. Quando rimarrà un solo filo, il palloncino verrà fatto scoppiare ( e la memoria liberata).

Spero di essermi spiegato, ma non trovo niente di meglio dei palloncini per rendere l'idea dei puntatori.

Loading