[C++] std::vector met parent-referenties *

Pagina: 1
Acties:

  • LauPro
  • Registratie: Augustus 2001
  • Laatst online: 30-11 12:59

LauPro

Prof Mierenneuke®

Topicstarter
In het programma heb ik een configuratie-klasse aangelegd (deze heet Config). Deze klasse beheert een controller en een vector met een root Map:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
class Config{
public:
    Config();
    
    // rootmap
    Map *rootMap;
    
    // vector met maps
    std::vector<Map> maps;
    
    // Controller to handle Maps
    MapController *mapcontroller;
}
De constructor initieert de rootMap en mapcontroller, zie hier:
C++:
1
2
3
4
5
6
7
Config::Config() {
    mapcontroller = new MapController(this);
    
    // set up the root map
    rootMap = new Map(this,0,"");
    maps.push_back(*rootMap);
}


Van de Config wordt door de applicatie eenmalig een instantie gemaakt (*oConfig). Deze wordt bij het aanmaken van maps in de mapcontroller weer doorgegeven aan een Map:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MapController {
public:
    MapController(Config *);
    void initMaps();

private:
    Config *oConfig;
}

MapController::MapController(Config *oConfig) {
    this->oConfig = oConfig;
}

void MapController::initMaps() {
    Map *oMap;
    std::string sName = "";
    // [..] zet name e.d.
    oMap = new Map(oConfig, oConfig->rootMap, sName);
    oConfig->maps.push_back(*oMap);
}
Definitie van Map met implementatie:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
class Map{
public:
    Map(Config*, Map*, std::string = "");
private:
    Config *oConfig;
    std::vector m_parent, m_name;
}

Map::Map(Config *config, Map* parent, std::string name) {
    oConfig = config;
    m_parent = parent;
    m_name = name;
}


In verdere code raakt de lokale (private) m_parent illegaal. Daarmee bedoel ik dat deze nog steeds verwijst naar het stukje geheugen dat bij de MapController was geallocceerd (of eigenlijk door Config zelf). Echter de std::vector wijzigt dit volgens mij naar gelang de vector gevuld wordt. Maar hoe kan ik dan in een Map een referentie leggen naar zijn parent(-Map)? Kan ik op de een of andere manier het interne ID van een waarde die de vector gebruikt achterhalen en die als referentie opgeven?

Het speelt dus bijvoorbeeld wanneer ik de std::vector maps itereer. In de debugger zie ik dan m_parent naar een geheugenadres verwijzen die hetzelfde is als bij initiatie ten tijden van initMaps() echter staat er nu garbage in zodat er een SEGFAULT of iets soortgelijks ontstaat.

[ Voor 6% gewijzigd door LauPro op 26-11-2006 03:47 ]

Inkoopacties - HENK terug! - Megabit
It is a war here, so be a general!


  • LauPro
  • Registratie: Augustus 2001
  • Laatst online: 30-11 12:59

LauPro

Prof Mierenneuke®

Topicstarter
Even een kleine update: was er nog steeds mee bezig. En heb het probleem nu eigenlijk kunnen bevestigen. Zodra er meer objecten in de vector komen raken de 'oude' parent-pointers out-of-sync.

edit:
Zo te zien ga ik een shared_ptr oid nodig hebben.

[ Voor 14% gewijzigd door LauPro op 26-11-2006 07:02 ]

Inkoopacties - HENK terug! - Megabit
It is a war here, so be a general!


  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 18:25

Creepy

Tactical Espionage Splatterer

Hoe itereer je maps en hoe benader je die m_parent? Ik zelf zou verwachten dat je de pointer naar de instantie van map in de vector zou stoppen i.p.v. de instantie zelf.
Als je vervolgens een map uit de vector haalt en een member van map via -> wil benaderen (wat kort is voor (*map). ) kan ik me voorstellen dat je garbage krijgt.

Overigens ken je in de constructor van Map een *Map (parent) direct aan een Vector toe (m_parent). Dat lijkt me ook niet helemaal te kloppen.

[ Voor 41% gewijzigd door Creepy op 26-11-2006 11:49 ]

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 13-09 00:05
Sorry - Map is niet geschikt om in een std::vector te plaatsen. std::vector elementen moeten Copyable zijn, maar een kopie van een parent Map is zelf geen parent Map.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 14:52

.oisyn

Moderator Devschuur®

Demotivational Speaker

Idd. Eerst alloceer je een nieuwe Map, en vervolgens zet je een kopie daarvan in een vector. Zoals met alle STL containers ownen ze zelf de objecten die je erin zet - als je dat niet wilt moet je pointers in de containers stoppen. En aangezien een vector intern een array gebruikt voor z'n data, en die array dus opnieuw moet alloceren als er elementen aan worden toegevoegd terwijl het niet meer past, veranderen dus ook de adressen van de objecten in de array. Als je dit niet wilt moet je een list gebruiken (of een deque als je alleen aan het begin of aan het einde insert).

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • LauPro
  • Registratie: Augustus 2001
  • Laatst online: 30-11 12:59

LauPro

Prof Mierenneuke®

Topicstarter
Dan zou ik dus std::vector<Map*> moeten gebruiken. Maar dan moet ik weer delete() aanroepen aan het einde van het programma. En dacht dat het juist werd afgeraden om vectors met pointers te gebruiken. (Aangezien er intern ook weer met een pointer wordt gelinkt.)

Dit is een voorbeeld:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
/// retrieve a Map based on a path
lpdMap* MapController::getMapFromPath(std::string path) {
    std::string tmp;
    for(std::vector<lpdMap>::iterator iter = oConfig->maps.begin(); iter != oConfig->maps.end(); iter++) {
        tmp = iter->getFullpath();
        if (tmp == path) {
            return &*iter;
        }
    }
    
    return NULL;
}
In het geval van std::vector<Map*> moet ik dan overal (*iter) gebruiken en return &**iter, vind ik onhandig of denk ik te moeilijk?

[ Voor 48% gewijzigd door LauPro op 26-11-2006 15:29 ]

Inkoopacties - HENK terug! - Megabit
It is a war here, so be a general!


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 14:52

.oisyn

Moderator Devschuur®

Demotivational Speaker

LauPro schreef op zondag 26 november 2006 @ 14:55:
Dan zou ik dus std::vector<Map*> moeten gebruiken. Maar dan moet ik weer delete() aanroepen aan het einde van het programma.
Dat moest sowieso al, je deed immers zelf een new Map(), dus dan moet je zelf ook die delete doen. En anders gebruik je een boost::ptr_vector, of je stopt er boost::shared_ptr's in.
En dacht dat het juist werd afgeraden om vectors met pointers te gebruiken. (Aangezien er intern ook weer met een pointer wordt gelinkt.)
:?
In het geval van std::vector<Map*> moet ik dan overal (*iter) gebruiken
Klopt, of je doet ldpMap* map = *iter; map->doeIets();
en return &**iter, vind ik onhandig of denk ik te moeilijk?
*iter geeft toch al juist die pointer? ;)

[ Voor 9% gewijzigd door .oisyn op 26-11-2006 15:17 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • LauPro
  • Registratie: Augustus 2001
  • Laatst online: 30-11 12:59

LauPro

Prof Mierenneuke®

Topicstarter
Dan heb ik een verkeerde aanname gedaan bij de vectors. Maar als ik het goed begrijp is het netter om bijv. de ptr_vector van Boost te gebruiken. Want STL levert in principe geen methode om objecten in een 'array/container' op te slaan zonder dat je je druk hoeft te maken om memory-management.

Inkoopacties - HENK terug! - Megabit
It is a war here, so be a general!


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 14:52

.oisyn

Moderator Devschuur®

Demotivational Speaker

Want STL levert in principe geen methode om objecten in een 'array/container' op te slaan zonder dat je je druk hoeft te maken om memory-management
Natuurlijk wel, alleen de containers zijn value-type containers. Er worden dus kopiën gemaakt als je een element toevoegt. Je hoeft dus niet een new te doen, en je moet ook geen vector gebruiken omdat je objecten in de container dan steeds een ander adres kunnen krijgen na een insert (waar een list dus weer geen last van heeft)

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Config{ 
public: 
    Config(); 
     
    // rootmap 
    Map *rootMap; 
     
    // vector met maps 
    std::list<Map> maps; 
     
    // Controller to handle Maps 
    MapController *mapcontroller; 
}

Config::Config() { 
    mapcontroller = new MapController(this); 
     
    // set up the root map 
    maps.push_back(Map(this,0,"")); 
    rootMap = &maps.back();
}


Alleen hiermee moet je oppassen - als je in de Map constructor z'n eigen 'this' pointer ergens registreert (bijvoorbeeld de map add zich als child bij z'n parent dmv m_parent->add(this)) dan loopt alles in de soep omdat er kopieën worden gemaakt. Hierdoor zou ik zelf wel een ptr_container prefereren.

[ Voor 44% gewijzigd door .oisyn op 26-11-2006 15:32 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • LauPro
  • Registratie: Augustus 2001
  • Laatst online: 30-11 12:59

LauPro

Prof Mierenneuke®

Topicstarter
Oke, hiermee ga ik even experimenteren. Het punt is dat er in code vrij veel wordt gedaan met die vector. Dus een wijzinging van std::vector naar std::list is nogal ingrijpend, maar met replace kan ik een heel eind.

Inkoopacties - HENK terug! - Megabit
It is a war here, so be a general!


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 14:52

.oisyn

Moderator Devschuur®

Demotivational Speaker

LauPro schreef op zondag 26 november 2006 @ 17:46:
Dus een wijzinging van std::vector naar std::list is nogal ingrijpend
Je moet je containers ook typedeffen, dan kun je het later zo veranderen.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Olaf van der Spek
  • Registratie: September 2000
  • Niet online
.oisyn schreef op zondag 26 november 2006 @ 18:29:
Je moet je containers ook typedeffen, dan kun je het later zo veranderen.
Waar is C++0x als je het nodig hebt? :)

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 30-11 00:17
LauPro schreef op zondag 26 november 2006 @ 17:46:
Oke, hiermee ga ik even experimenteren. Het punt is dat er in code vrij veel wordt gedaan met die vector. Dus een wijzinging van std::vector naar std::list is nogal ingrijpend, maar met replace kan ik een heel eind.
Zou eigenlijk niet zo mogen zijn. ( Of iig niet al te veel impact ). De STL zit zo in elkaar dat containers vrij makkelijk uit te wisselen zijn.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 14:52

.oisyn

Moderator Devschuur®

Demotivational Speaker

Tenzij je overal std::vector<Map*>::iterator e.d. gebruikt ;)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 30-11 00:17
.oisyn schreef op maandag 27 november 2006 @ 11:19:
Tenzij je overal std::vector<Map*>::iterator e.d. gebruikt ;)
Tja dan wordt het iets bewerkelijker

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • LauPro
  • Registratie: Augustus 2001
  • Laatst online: 30-11 12:59

LauPro

Prof Mierenneuke®

Topicstarter
Inmiddels wel typedefs gebruikt, en met replace-in-all-files gaat dat redelijk vlot. Maar ging me meer om std::vector<Map*> vs std::list<Map>. Dan moet je bij itereren bijv. *iter gebruiken ipv iter. En dat kan bewerkelijk zijn. Kwestie van alles goed inrichten dus.

Inkoopacties - HENK terug! - Megabit
It is a war here, so be a general!

Pagina: 1