[C++] Verschillende objecten in één vector / array

Pagina: 1
Acties:

  • st0p
  • Registratie: April 2004
  • Laatst online: 19-07-2024
Okay, ik ben nieuw in het c++ programmeren en ik zit met een behoorlijke noob vraag. Ik ben bezig met een soort stand-alone winamp plugin, een progje wat dus beelden genereert aan de hand van de muziek die je afspeelt. Nou heb ik een aantal "vormen" gedefinieerd, zoals cirkel, golfvorm en driehoek. Deze klassen zijn allemaal afgeleid van de hoofdklasse Vorm en ze hebben allemaal de methode Draw() (een overloaded functie die ook al in de in de klasse Vorm voorkomt.)

het probleem is dat ik van te voren niet weet welke vormen er gebruikt gaan worden, en ook niet in welke aantallen. Ik heb al een aantal opties geprobeerd:
- ik kan een vector <Vorm> Vormen maken en daar verschillende vormen door elkaar heen in schuiven. Alleen, als ik dan de array Vormen doorloop en voor ieder object de Draw() methode aanroep dan word de Draw() van Vorm gebruikt in plaats van bijvoorbeeld VormCirkel of VormDriehoek
- ik heb geprobeerd van Vorm een template te maken met een embedded object wat dan zou bepalen of het een Cirkel dan wel een Driehoek is. Dit werkt, alleen dan kan ik ze weer niet in één vector kwijt aangezien die dan als vector <Vorm <VormCirkel>> Vormen gedefinieerd moet worden. en dat is dus niet de bedoeling want ik wil als het kan door een array heenlopen en daar voor ieder object de overloaded Draw() aanroepen (dus de draw van VormCirkel/VormDriehoek ipv de draw van Vorm).

Goed, ik heb thuis even geen internet dus ik tik dit van mijn werk maar ik hoop dat ondanks het ontbreken van stukjes code enigzins te volgen is. is wat ik wil mogelijk of zit ik helemaal op het verkeerde pad? ik heb namelijk weinig zin om een "boekhoud" klasse te maken die bijhoud wat voor type vormen er gebruikt worden.

  • whoami
  • Registratie: December 2000
  • Laatst online: 09:11
Wat jij nodig hebt is polymorphisme;

Aangezien al jouw 'vormen' afleiden van een hoofdklasse Vorm, en ze allemaal al een methode 'overriden' Draw, ben je al halfweg.
Die Draw method moet je virtual maken, en dan kan je al uw vormen mooi in een array of een vector stoppen die objecten van het type 'Vorm' moet bevatten.
Je kan dan gewoon over die array loopen, en voor ieder object de Draw method aanroepen.

https://fgheysels.github.io/


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 07-05 22:49

curry684

left part of the evil twins

Sla een (smart) pointer naar het base object op in de vector ipv het object Vorm er zelf in te zetten (gezien het feit dat die fysiek van het type Vorm is en er dus per definitie geen afgeleide klasse in past wegens vtables en eventuele extra fields :) ).

Professionele website nodig?


  • BM
  • Registratie: September 2001
  • Laatst online: 10:36

BM

Admin Softe Goederen
edit:

de antwoorden hierboven zijn kwalitatief wat beter

[ Voor 94% gewijzigd door BM op 02-05-2005 11:52 ]

Xbox
Even the dark has a silver lining | Te koop: Chigee AIO-6 + toebehoren


  • Icelus
  • Registratie: Januari 2004
  • Niet online
Je zou de functie Draw abstract kunnen maken in de basisklasse.
Vervolgens maak je een vector met daarin pointers naar deze basisklasse.

Developer Accused Of Unreadable Code Refuses To Comment


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 07-05 22:49

curry684

left part of the evil twins

whoami schreef op maandag 02 mei 2005 @ 11:48:
Wat jij nodig hebt is polymorphisme;

Aangezien al jouw 'vormen' afleiden van een hoofdklasse Vorm, en ze allemaal al een methode 'overriden' Draw, ben je al halfweg.
Die Draw method moet je virtual maken, en dan kan je al uw vormen mooi in een array of een vector stoppen die objecten van het type 'Vorm' moet bevatten.
Je kan dan gewoon over die array loopen, en voor ieder object de Draw method aanroepen.
Helaas werkt dit dus niet als je een vector hebt omdat objecten daar middels copy constructors in geplaatst worden en ze dus fysiek van het type Vorm zijn en niet van het afgeleide type. Wat direct een goede reden is om Vorm abstract te maken door Draw(...) pure virtual te maken, dan kun je deze programmeerfout niet maken.

Professionele website nodig?


Verwijderd

Je kunt toch gewoon Vorm vorm = new Vierkant(); doen, waarbij automatisch de juiste Draw() wordt aangeroepen :? Mits Vierkant een afgeleide is van Vorm, is dit toch gewoon toegestaan.

[ Voor 26% gewijzigd door Verwijderd op 02-05-2005 11:54 ]


  • whoami
  • Registratie: December 2000
  • Laatst online: 09:11
Verwijderd schreef op maandag 02 mei 2005 @ 11:53:
Je kunt toch gewoon Vorm vorm = new Vierkant(); doen, waarbij automatisch de juiste Draw() wordt aangeroepen :? Mits Vierkant een afgeleide is van Vorm, is dit toch gewoon toegestaan.
Ja, als je 't zo doet.
Wat curry wil zeggen (denk ik), is dat dat dus in C++ enkel werkt als je die objecten op de heap maakt, en het niet werkt als je ze op de Stack maakt ?

https://fgheysels.github.io/


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 07-05 22:49

curry684

left part of the evil twins

Verwijderd schreef op maandag 02 mei 2005 @ 11:53:
Je kunt toch gewoon Vorm vorm = new Vierkant(); doen, waarbij automatisch de juiste Draw() wordt aangeroepen :? Mits Vierkant een afgeleide is van Vorm, is dit toch gewoon toegestaan.
Nee, met die code roep je een copy constructor aan :) Je mist een sterretje:
C++:
1
2
Vorm* vorm = new Vierkant();
vorm->Draw(); 

En dan moet Draw dus virtual of pure virtual zijn aub :)

Professionele website nodig?


  • st0p
  • Registratie: April 2004
  • Laatst online: 19-07-2024
in ieder geval alvast bedankt voor de reacties!
whoami schreef op maandag 02 mei 2005 @ 11:48:
Wat jij nodig hebt is polymorphisme;

Aangezien al jouw 'vormen' afleiden van een hoofdklasse Vorm, en ze allemaal al een methode 'overriden' Draw, ben je al halfweg.
Die Draw method moet je virtual maken, en dan kan je al uw vormen mooi in een array of een vector stoppen die objecten van het type 'Vorm' moet bevatten.
Je kan dan gewoon over die array loopen, en voor ieder object de Draw method aanroepen.
dit is dus wat ik in eerste instantie ook dacht (en Draw is idd al virtual). alleen als ik dat doe, dan roept hij bij het doorlopen van de vector de functie Draw van Vorm aan (die op dit moment leeg is) ipv die van VormCirkel/VormDriehoek (die dus wel output genereren.)
Sla een (smart) pointer naar het base object op in de vector ipv het object Vorm er zelf in te zetten
hier heb ik ook al aan gedacht alleen is het me nog niet gelukt om dat werkend te krijgen.
(gezien het feit dat die fysiek van het type Vorm is en er dus per definitie geen afgeleide klasse in past wegens vtables en eventuele extra fields ).
hier raak je me kwijt... ik weet wat de vtable is maar dit volg ik even niet...
Je zou de functie Draw abstract kunnen maken in de basisklasse. Vervolgens maak je een vector met daarin pointers naar deze basisklasse.
wat bedoel je precies met abstract maken? het is nu al een virtual functie, of bedoel je iets heel anders?

  • curry684
  • Registratie: Juni 2000
  • Laatst online: 07-05 22:49

curry684

left part of the evil twins

whoami schreef op maandag 02 mei 2005 @ 11:55:
[...]


Ja, als je 't zo doet.
Wat curry wil zeggen (denk ik), is dat dat dus in C++ enkel werkt als je die objecten op de heap maakt, en het niet werkt als je ze op de Stack maakt ?
Kijk het punt is dat Vorm bijvoorbeeld 12 bytes (positie + vtable) is, en Vierkant 20 (12 + lengte +breedte) en Cirkel 16 (12 + radius). Een vector is niets anders dan een blok geheugen van achter mekaar geplakte classes, dus element 5 van een vector<Vorm> staat op offset 5*12. Er kan dus geen afgeleide class in, en als je het probeert krijg je een copy constructor invocation (die ik overigens protected zou maken op Vorm om dit te voorkomen). Als je een (smart) pointer gebruikt kan het wel dus (standard polymorphism).

Professionele website nodig?


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 07-05 22:49

curry684

left part of the evil twins

M'n C++ is toch wat roestig, dus even het punt illustreren met wat voorbeeldcode:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include <vector>
#include <iostream>

using namespace std;

class Vorm
{
public:         // Methods
  virtual void Draw() = 0;
  inline int Left() const { return m_Left; }
  inline int Top() const { return m_Top; }
  
protected:      // Methods
  Vorm(int p_Left, int p_Top)
  {
    m_Left = p_Left;
    m_Top = p_Top;
  }
  
  Vorm(const Vorm &p_Vorm);

  void operator =(const Vorm &p_Vorm);

private:        // Properties
  int m_Left;
  int m_Top;                                    
};

class Vierkant : public Vorm
{
public:         // Methods
  Vierkant(int p_Left, int p_Top, int p_Width, int p_Height)
    : Vorm(p_Left, p_Top)
  {
    m_Width = p_Width;
    m_Height = p_Height;  
  }

  Vierkant(const Vierkant &p_Vorm);
  void operator =(const Vierkant &p_Vorm);
  
  virtual void Draw()
  {
    cout << "Vierkant: (" << Left() << ',' << Top() << ")-(" << Right() << ',' << Top() << ")" << endl;
  }
  
  inline int Width() const { return m_Width; }
  inline int Height() const { return m_Height; }
  inline int Right() const { return m_Width + Left(); }
  inline int Bottom() const { return m_Height + Top(); }  

private:        // Properties
  int m_Width;
  int m_Height;  
};

class Cirkel : public Vorm
{
public:         // Methods
  Cirkel(int p_Left, int p_Top, int p_Radius)
    : Vorm(p_Left, p_Top)
  {
    m_Radius = p_Radius;  
  }

  Cirkel(const Cirkel &p_Vorm);
  void operator =(const Cirkel &p_Vorm);
  
  virtual void Draw()
  {
    cout << "Cirkel: (" << Left() << ',' << Top() << "), radius " << Radius() << endl;
  }
  
  inline int Radius() const { return m_Radius; }  

private:        // Properties
  int m_Radius;
};

int main()
{
  vector<Vorm*>             l_Vormen;
  vector<Vorm*>::iterator   l_Iterator;
  
  l_Vormen.push_back(new Vierkant(5, 10, 15, 20));
  l_Vormen.push_back(new Cirkel(35, 40, 15));
  l_Vormen.push_back(new Vierkant(85, 10, 35, 10));
  l_Vormen.push_back(new Cirkel(135, 30, 25));
  for(l_Iterator = l_Vormen.begin(); l_Iterator != l_Vormen.end(); l_Iterator++)
  {
    (*l_Iterator)->Draw();
    delete *l_Iterator;
  }
  cin.get();
  return 0;
}

Ik hoop dat dit een en ander beetje illustreert :)

Professionele website nodig?


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 07-05 10:04
Het probleem dat jij hebt heet 'object splicing' en ontstaat doordat ( zoals al werd vermeld ) de stl containers zijn ontworpen als opslag van 'value types'. Dwz, en wordt een kopie gemaakt van het object dat je erin stopt. En afgeleide objecten passen nou eenmaal vaak niet in dezelfde ruimte als waar het basisobject wel in past.

De oplossing is om een (smart)pointer te gebruiken. Echter, de smart pointer die in de stl zit kun je daarvoor niet gebruiken !

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.


  • st0p
  • Registratie: April 2004
  • Laatst online: 19-07-2024
hmmm interessant... dankje wel alvast! _/-\o_

ik heb hem hier op mn werk ff gecompileerd en het werkt idd zoals ik in gedachten had. nou moet ik uit gaan zoeken wat dan een vector::iterator is (nog niet eerder gezien) en waarom je in je deze constructor gebruikt:

Vierkant(const Vierkant &p_Vorm);

(dit is eigenlijk een verkapte hint om uitleg ;) ) maar okay, ik zal eens in mijn boek gaan zoeken om erachter te komen wat je nou precies doet (ik hou er niet van om code klakkeloos te kopieren zonder het te begrijpen)

  • curry684
  • Registratie: Juni 2000
  • Laatst online: 07-05 22:49

curry684

left part of the evil twins

st0p schreef op maandag 02 mei 2005 @ 13:26:
(ik hou er niet van om code klakkeloos te kopieren zonder het te begrijpen)
En dat is de instelling zoals we het hier graag zien, en waarom ik je expres een stuk uncommented code gaf ;)

De constructor waar je op doelt is niet geimplementeerd zoals je ziet, en wordt dus ook niet gebruikt (anders had je een linker error gehad). De reden dat hij er is is dezelfde als waarom er een operator= is: omdat ik a) de default implementaties wil voorkomen (ondanks dat het in dit geval toevallig geen probleem is) en b) ik ze specifiek protected heb gemaakt in de base-class om per-ongeluk copies te voorkomen. Wat niet mag moet je niet toestaan, en als je de copy-constructor en de operator= niet specifiek definieert maakt de compiler er public default implementaties van :)

Professionele website nodig?


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Strafpunten overigens voor delete *iterator; :P
Sowieso aftrek voor delete buiten een dtor, en extra omdat je invalide pointers achterlaat in een vector.
Netter:
C++:
1
2
3
4
5
6
7
 for( vector<Vorm*>::iterator vi = l_Vormen.begin(); vi != l_Vormen.end(); vi++)
  {
    (*vi)->Draw();
    Vorm* tmp = 0;
    swap(*vi,tmp);
    delete tmp;
  } 

Alhoewel je voor zoiets ook een class deleter { ... } kunt maken, en dan met transform de zaak opruimen. Maar dat is het verschil tussen een 9.5 en een 10, zeg maar.

[ Voor 8% gewijzigd door MSalters op 02-05-2005 17:53 ]

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


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 07-05 22:49

curry684

left part of the evil twins

MSalters schreef op maandag 02 mei 2005 @ 17:53:
Strafpunten overigens voor delete *iterator; :P
:+
Sowieso aftrek voor delete buiten een dtor, en extra omdat je invalide pointers achterlaat in een vector.
Dat mag imho als de vector 1 regel later toch buiten scope gaat ;)

Professionele website nodig?


  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

even off-topic:
leer ik ook weer veel van bij... dit leren ze je niet op school :(

ASSUME makes an ASS out of U and ME

Pagina: 1