Toon posts:

[C++] Array van class*

Pagina: 1
Acties:

Verwijderd

Topicstarter
Eff waarschijnlijk een simpel vraagje!

Ik krijg in een programma van mij, de hele tijd een access violation. Ik heb het probleem even simpel gemaakt. Het is dus een elementair iets wat ik fout doe.

code:
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
class A {

public:
    A() { };

    virtual void myFunction() = 0;
};

class B : public A {
private:

    int anotherValue;

public:
    B() { anotherValue = 0; }

    void myFunction() { anotherValue++; }
};

int main(int argc, char* argv[])
{
    A* myA;

    myA = new B[100];
    myA[2].myFunction();    //  <---------------GEEN ACCESS VIOLATION! 
    myA[1].myFunction();    //  <---------------ACCESS VIOLATION!
    return 1;
}


Ik wil dus een pointer naar een array van B classes en geen pointer naar een array van pointers naar een B class.

Thanx...

  • pjvandesande
  • Registratie: Maart 2004
  • Laatst online: 21-05 14:59

pjvandesande

GC.Collect(head);

De objecten array zijn toch nog niet geinitizileerd?

edit:
Nee dat is het zeker niet... Questa gaat koffie pakken O-)

[ Voor 45% gewijzigd door pjvandesande op 14-04-2004 09:54 ]


Verwijderd

Topicstarter
Waarom niet?

De standaard contructor wordt toch voor elk element aangeroepen... Die zet het attribuut op 0.

  • whoami
  • Registratie: December 2000
  • Laatst online: 23:32
Volgens mij niet.
Je hebt nu wel ruimte gealloceerd voor 100 pointers naar objecten, maar moet je nog niet dit doen:

code:
1
myA[0] = new B();

:?

Je moet afaik dus nog je object zelf alloceren.

Je kunt het eens testen:
code:
1
2
3
4
if( myA[0] == null )
{
  cout << "spul is null";  
}

[ Voor 10% gewijzigd door whoami op 14-04-2004 09:57 ]

https://fgheysels.github.io/


Verwijderd

(Ik neem aan dat dit c++ is)
Je maakt wel een array aan met plek voor 100 objecten van class B maar het array is leeg en bevat nog geen objecten.

Je moet dus nog zo iets doen:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
int main(int argc, char* argv[])
{
    A* myA;

    myA = new B[100];
    for (int i=0; i<100; i++) {
      myA[i]= new B();
    }

    //
    // rest van je code
    //
}

edit:
whoami typt net wat sneller

[ Voor 7% gewijzigd door Verwijderd op 14-04-2004 10:02 ]


  • marino_centrino
  • Registratie: November 2003
  • Laatst online: 05-04 20:14
Ja, je zult de objecten in de array eerst moeten initialiseren voordat je er functies op aanroept. Anders is het gedrag zeer onvoorspelbaar, zoals je al aangeeft. Objecten in een araay worden volgens mij niet standaard geïnitialiseerd.
Dus eerst even:

C++:
1
 for(int i=0;i<100;i++) myA[i] = B(); 


voordat je functies op de objecten in die array gaat aanroepen

edit: (oeps, net te laat)

[ Voor 5% gewijzigd door marino_centrino op 14-04-2004 10:00 ]


Verwijderd

Topicstarter
Helaas...

Dit werkt namelijk ook niet:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
int main(int argc, char* argv[])
{
    A* myA;

    myA = new B[100]();
    for (int i=0; i<100; i++)
    {
        myA[i] = new B();
    }
    myA[2].myFunction();
    myA[1].myFunction();
    return 1;
}


Levert de volgende (vrij logische) error:
error C2679: binary '=' : no operator defined which takes a right-hand operand of type 'class B *' (or there is no acceptable conversion)

dit betekend dat hij myA[i] dus ziet als een object en niet een pointer, new levert natuurlijk een pointer op.

  • marino_centrino
  • Registratie: November 2003
  • Laatst online: 05-04 20:14
Je moet ook niet
code:
1
 new B()
maar gewoon
code:
1
 B()
aanroepen.

Verwijderd

Dit alloceert alleen ruimte voor 100 objecten:
C++:
1
 B *x = new B[100]; 


Dit roept 100 keer de constructor van B aan:
C++:
1
 B x[100]; 


Edit: blijkbaar niet dus, ook de bovenste variant roept 100 keer de constructor van B aan

[ Voor 24% gewijzigd door Verwijderd op 14-04-2004 10:48 ]


Verwijderd

Topicstarter
Ik heb natuurlijk het een en ander geprobeerd voor dat ik iets post :)

Dit werkt ook niet:

int main(int argc, char* argv[])
{
A* myA;

myA = new B[100]();
for (int i=0; i<100; i++)
{
myA[i] = B();
}
myA[2].myFunction();
myA[1].myFunction();
return 1;
}

In een vlaag van verstandsverbijstering d8 ik net aan de operator [] te implementeren? of begin ik door de draaien? ;)

p.s. Ik kon het topic onderwerp niet meer aanpassen naderhand, maar het gaat idd om C++ ( in Visual studio )

Thanx voor de hulp so far!

Verwijderd

Topicstarter
Verwijderd schreef op 14 april 2004 @ 10:04:
Dit alloceert alleen ruimte voor 100 objecten:
C++:
1
 B *x = new B[100]; 


Dit roept 100 keer de constructor van B aan:
C++:
1
 B x[100]; 
Het probleem is dat ik een pointer naar A moet defineren. Er zijn in mijn programma nog meer classes van de abstracte class afgeleid.

Verwijderd

Levert de volgende (vrij logische) error:
error C2679: binary '=' : no operator defined which takes a right-hand operand of type 'class B *' (or there is no acceptable conversion)
Mijn c++ kennis is wat roestig (met name het hele pointer gedoe) omdat ik nu alleen maar Java en C# gebruik. Maar is het niet een kwestie van een beetje casten naar het juiste type.
zoiets:
C++:
1
    myA[i] = &(A*) new B();


/me Rmk gaat zijn c++ boeken maar weer eens opzoeken

  • Shadowman
  • Registratie: Januari 2002
  • Niet online
Verwijderd schreef op 14 april 2004 @ 10:11:
[...]


Mijn c++ kennis is wat roestig (met name het hele pointer gedoe) omdat ik nu alleen maar Java en C# gebruik. Maar is het niet een kwestie van een beetje casten naar het juiste type.
zoiets:
C++:
1
    myA[i] = &(A*) new B();


/me Rmk gaat zijn c++ boeken maar weer eens opzoeken
Nee dat is niet nodig ;).
B(); returnt hetzelfde als jij wilt proberen te casten en een class hoeft bij overerving niet naar de parentclasse te worden gecast. Foutmelding kwam alleen hierdoor:
myA[1] < returnt een reference en geen pointer.

Is trouwens het gebruikmaken van een std::vector geen optie :?.

edit:
B*myA; werkt wel.

edit2:
zonder for-loop overigens dus zo:
code:
1
2
3
4
5
6
7
8
9
int main(int argc, char* argv[])
{
    B* myA;

    myA = new B[100];
    myA[2].myFunction();    //  <---------------GEEN ACCESS VIOLATION! 
    myA[1].myFunction();    //  <---------------ACCESS VIOLATION!
    return 1;
}

[ Voor 20% gewijzigd door Shadowman op 14-04-2004 10:31 ]


Verwijderd

Topicstarter
Wanneer ik virtual in de parent class weg laat werkt het wel, dus ik kan wel vooruit. Maar het is natuurlijk niet hoe het zich hoort. Dus ideën zijn nog zekers welkom, ik wil wel eens weten hoe het zit.

code:
1
B* myA
Werkt wel ja. Omdat je het hele inheritance verhaal overslaat. Daar zit het hem net in.

In het programma wat ik maak is een class ChannelManager, Die is eigenaar van x aantal Channels. "CChannel" is dus de parent class. Alleen kun je geluiod afspelen van / naar geheugen of van / naar disk. Dus zijn er 2 subclasses nodig, die via dezelfde interface werken. "CDiskChannel" en "CMemChannel".

Het lijkt mij toch een triviale zaak. Niet iets wat nog nooit voor is gekomen toch?

Ik maak die functie voor nu dus even niet virtueel, maar dat is maar een tijdelijke oplossing. Ik was bang dat als ik wat gasten het hier zou vragen ze mij zouden uitlachen.... :'( Blijkbaar is het toch niet iets wat iedereen zomaar uit de mouw schudt.

Verwijderd

Heb even een testje gedaan.
C++:
1
myA = new B[100];

Roept WEL de constructor voor B 100 keer aan.

Er lijkt iets fout te gaan met het vinden van myFunction in B als er een pointer naar de baseclass wordt gebruikt, want als myA van het type B* is gaat het wel goed. :?

  • schoene
  • Registratie: Maart 2003
  • Laatst online: 21:51
Waarom maak je geen arrays van pointers aan?
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    A** myA;

    *myA = new B[100];

    for (int i = 0; i < 100; ++i)
        myA[i] = new B ();

    myA[2]->myFunction();    
    myA[1]->myFunction();    

    for (int i = 0; i < 100; ++i)
        delete myA[i];

    delete [] myA;

    return 1;

  • SWfreak
  • Registratie: Juni 2001
  • Niet online
Wat schoene voorstelt werkt inderdaad perfect. De reden zit 'm volgens mij in hoe de vtable er in betrokken wordt. Kennelijk gebeurt dat bij myA[2].myFunction() niet en bij myA[2]->myFunction() wel. Beetje vaag nog allemaal...

  • whoami
  • Registratie: December 2000
  • Laatst online: 23:32
Je moet idd de -> operator gebruiken ipv de . operator.

https://fgheysels.github.io/


Verwijderd

Inderdaad, het heeft er denk ik mee te maken dat myA een pointer is naar een object van de baseclass en niet naar een ARRAY van objecten. Een array en een pointer naar een object is denk ik anders voor het dynamic binden.

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Oi, dit zijn wel erg veel foute antwoorden.

Het probleem is dat je een Base* en een Derived* alleen mag mixen/casten als het pointers naar een enkel object zijn. Een Base* kun je niet gebruiken om naar een Derived[] te pointen. Het probleem is dat Base* +1 de pointer met sizeof(Base) verhoogt om bij het volgende Base object te komen, en aangezien sizeof(Base) != sizeof( Derived ) kom je dus niet bij het volgende Derived object uit, maar halverwege het eerste Derived object.

Base* [1] heeft hetzelfde probleem, want dat is *( Base* +1 ).

De correcte code declareert dus B* myB = new B[100];
Dat is de enige declaratie van een
pointer naar een array van B classes

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


  • schoene
  • Registratie: Maart 2003
  • Laatst online: 21:51
dat verklaart waarom ik bij mij nog steeds fouten krijg met mijn code. weer iets bijgeleerd, en het klinkt nog logisch ook >:)

Verwijderd

Topicstarter
Dat dat gaat snap ik...

Ik wou gewoon weten waarom dit niet werkt. Omdat je nu eigenlijk een andere structuur implementeert, dan je eigenlijk wilt. Ik had gehoopt het programma iets te kunnen laten winnen op gebied van performance door de extra pointers weg te kunnen laten.

Vandaar deze regel in mijn 1ste post... :D
Verwijderd schreef op 14 april 2004 @ 09:47:

Ik wil dus een pointer naar een array van B classes en geen pointer naar een array van pointers naar een B class.

Thanx...

Verwijderd

Topicstarter
MSalters schreef op 14 april 2004 @ 11:04:
Oi, dit zijn wel erg veel foute antwoorden.

Het probleem is dat je een Base* en een Derived* alleen mag mixen/casten als het pointers naar een enkel object zijn. Een Base* kun je niet gebruiken om naar een Derived[] te pointen. Het probleem is dat Base* +1 de pointer met sizeof(Base) verhoogt om bij het volgende Base object te komen, en aangezien sizeof(Base) != sizeof( Derived ) kom je dus niet bij het volgende Derived object uit, maar halverwege het eerste Derived object.

Base* [1] heeft hetzelfde probleem, want dat is *( Base* +1 ).

De correcte code declareert dus B* myB = new B[100];
Dat is de enige declaratie van een
[...]
Thanx, inderdaad logisch als je het zo hoort. Ja moet het maar net weten hoe het zit.

mzzls!

  • schoene
  • Registratie: Maart 2003
  • Laatst online: 21:51
wat ik eigenlijk bedoelde, na een beetje testen was dit:
(en lettend op de post van msalters werkt dit nu wel: je maakt een array van A-objecten)

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
    A** myA;

    myA = new A*[100];

    for (int i = 0; i < 100; ++i)
        myA[i] = new B ();

    myA[2]->myFunction();
    myA[1]->myFunction();

    for (int i = 0; i < 100; ++i)
        delete myA[i];

    delete [] myA;

Verwijderd

Topicstarter
schoene schreef op 14 april 2004 @ 11:20:
wat ik eigenlijk bedoelde, na een beetje testen was dit:
(en lettend op de post van msalters werkt dit nu wel: je maakt een array van A-objecten)

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
    A** myA;

    myA = new A*[100];

    for (int i = 0; i < 100; ++i)
        myA[i] = new B ();

    myA[2]->myFunction();
    myA[1]->myFunction();

    for (int i = 0; i < 100; ++i)
        delete myA[i];

    delete [] myA;
Jah ik vond het al raar dat SWfreak het perfect werkend vond ;)
Dit is dan inderdaad de enige(?) methode om Globaal gezien de superclass te definieren en onderliggend de sub classen aan te spreken.

  • SWfreak
  • Registratie: Juni 2001
  • Niet online
Verwijderd schreef op 14 april 2004 @ 11:30:
[...]


Jah ik vond het al raar dat SWfreak het perfect werkend vond ;)
Iets te snel gelezen B)

[ Voor 3% gewijzigd door SWfreak op 14-04-2004 11:33 ]


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
schoene schreef op 14 april 2004 @ 11:20:
wat ik eigenlijk bedoelde, na een beetje testen was dit:
(en lettend op de post van msalters werkt dit nu wel: je maakt een array van A-objecten)

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
    A** myA;

    myA = new A*[100];

    for (int i = 0; i < 100; ++i)
        myA[i] = new B ();

    myA[2]->myFunction();
    myA[1]->myFunction();

    for (int i = 0; i < 100; ++i)
        delete myA[i];

    delete [] myA;
Nou, eigenlijk is dit een array van A*, een array van pointers dus. In vergelijking met de B* = new B[100] vreet dit geheugen; misschien wel 0,08% van je geheugen inplaats van 0,01%. 8)
Wat kwalijker is is dat je alle memory management nog steeds zelf moet doen; een std::vector<B> is veel makkelijker - of in de lijn van jouw design, een std::vector< boost::shared_ptr< A > >

Overigens, als ik het zou doen, en geheugen zou relevant zijn, dan zou het iets worden als

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
class A {
  // alles non-virtual ! Is kleiner
};

class B : public A {
  // ...
}

template< int SZ > // fixed-size
class A_Array {
  // Data in derived
public:
  virtual A* operator[]  ( int i ) = 0;
  // en const-overloads
  virtual /Type/ /A-function/ ( int i, /A-Args/ ) = 0; // voor alle functies in A
}

template< typename T, int SZ >
class Array : public A_Array<SZ> {
  T[SZ] data;
public:
  virtual T* operator[] ( int i ) { assert( (unsigned)i < SZ ); return data[i]; );
  virtual /Type/ /A-function/ ( int i, /A-Args/ )  { // voor alle functie in A
    return data[i]->/A-function/ ( /A-Args/ ); 
  }
};

Een Array<B, 100> kun je dan casten naar een A_Array<100>, en een
A_Array<100> heeft een operator[] die gewoon een A* teruggeeft.
Alle functies van A zijn beschikbaar via A_Array, en als dat een Array<B,100>
was dan krijg je ook de override van die functie uit B - ook al was het
een non-virtual. De truc is dat de virtual resolve in je ene Array class
gebeurt, en niet in alle 100 A objecten (de memory optimalisatie).

Strikt genomen heb je de base class dus niet eens nodig! Het is alleen een compacte vorm om gemeenschappelijke code/data voor classes B1, B2 etc. in op te slaan.

[ Voor 44% gewijzigd door MSalters op 14-04-2004 12:47 ]

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


  • schoene
  • Registratie: Maart 2003
  • Laatst online: 21:51
Is het dan weer niet zo, dat als je veel templates gebruikt, dit ook nadelig is?
Indien je in 2 verschillende files een bepaalde template zou gebruiken, met hetzelfde type, je dan toch 2 keer die klasse linkt in je exe? Ik weet niet meer precies hoe het zit, kheb dit ooit eens gelezen in CT magazine dacht ik

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Nah, je compileert 'm waarschijnlijk twee keer ( tenzij precompiled headers of template caches of etcetera - best gangbaar ) maar de linker zal de tweede kopie moeten elimineren. De reden is dat &foo<int> == &foo<int>, ook al is de linkerkant in de ene .cpp berekend, en de rechterkant in een andere .cpp. Met twee kopieen zou je twee adressen hebben.

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

Pagina: 1