Zobrazit předchozí téma :: Zobrazit následující téma |
Autor |
Zpráva |
kerekes
Založen: 29. 07. 2007 Příspěvky: 57
|
Zaslal: 2. březen 2008, 19:40:55 Předmět: Problem s virtualnou dedicnostou |
|
|
Zdravim,
povedzme ze triedy B, C dedia od A a D dedi od B,C (kosostvorec).
Pouzil som teda virtualnu dedicnost aby som zabranil duplikacii A v D.
Avsak pri adresacii pola D-ciek skonvertovanych na C-cka to pada.
Pri debugovani som zistil ze zrejme adresovanie prvkov pola D skonvertovanych na pole C dava zle adresy. A mna by zaujimalo preco a co sa s tym da robit aby sa to spravalo korektne.
ukazka:
kód: |
class A
{
float a;
public:
void setA(float a)
{
this->a=a;
};
};
class B: virtual public A
{
};
class C: virtual public A
{
};
class D: public B, public C
{
};
int main(void)
{
D* d=new D[50];
for(unsigned int i=0; i<50; i++)
{
// OK
((C*)&(d[i])) ->setA(10.0f);
}
C* c=d;
for(unsigned int i=0; i<50; i++)
{
C* ci = &(c[i]);
// Crash, i=1
ci ->setA(10.0f);
}
return 0;
}
|
Diki za rady. |
|
Návrat nahoru |
|
 |
Marek

Založen: 28. 07. 2007 Příspěvky: 1782 Bydliště: Velká Morava
|
Zaslal: 2. březen 2008, 21:27:31 Předmět: |
|
|
Takové věci s pointerama nedělat, navíc s virtuální dědičností je to ještě komplikovanější. Doporučuju rovnou zapomenout na pole objektů společně s dědičností, to prostě není zdravé. Napadá mě tohle zdůvodnění:
Nechť je proměnná Trida *pole; pak pole[i] je to samé co *(pole+i), což je to samé co *((Trida*)((char*)(pole) + sizeof(Trida)*i)).
Problém je, když máš takhle 2 pole typu Trida1* a Trida2*, kde sizeof(Trida1) < sizeof(Trida2). Pak menší třída zabere méně paměti a tedy v tom poli zabere méně bajtů, tzn. i-tý prvek typu Trida1 bude v paměti dříve než i-tý prvek typu Trida2 (právě kvůli tomu přičítání sizeof), tzn. budeš přistupovat úplně někam jinam.
To by ještě tak nevadilo, protože tvoje třída C je menší než D, takže to projde, i když to může poškodit některé objekty v poli (třeba vftable nebo tak něco). Ale ono to spadlo okamžitě právě kvůli té virtuální dědičnosti. Ta je implementovaná tak, že místo aby třída měla objekt v sobě jako standardní dědičnost, která by vypadala nějak takto:
kód: |
struct D
{
struct C
{
struct A {};
}
struct B
{
struct A {};
}
}; |
Tak virtuální dědičnost dává do tříd vždy pointer a vypadá to nějak takto:
kód: |
struct D
{
struct C
{
struct A *ptr;
}
struct B
{
struct A *ptr;
}
struct A {};
}; |
Tím se zajistí, že ta třída tam bude jen jednou. Problém byl v tom, že ses z třídy C snažil použít třídu A, ale kvůli tomu indexování v poli jsi místo pointeru na A dostal něco jinýho a to jsi dereferencoval. A proto ta chyba.
Takže doporučuju nepoužívat pole na děděné objekty a zároveň přetypování nahoru a dolů v dědičné hierarchii. Používej raději pole pointerů na ně, tzn. třeba vector<D*>. Potom do typu vector<C*> musíš každý prvek ručně přiřadit, aby se korektně provedlo přetypování z D* na C*. _________________ AMD Open Source Graphics Driver Developer |
|
Návrat nahoru |
|
 |
kerekes
Založen: 29. 07. 2007 Příspěvky: 57
|
Zaslal: 2. březen 2008, 23:06:16 Předmět: |
|
|
Dik, nejak to predesignujem. |
|
Návrat nahoru |
|
 |
Al
Založen: 23. 10. 2007 Příspěvky: 196
|
Zaslal: 5. březen 2008, 09:57:18 Předmět: |
|
|
Správně se v C++ má u objektů používat jen pole pointerů. Zhruba takto:
kód: |
D** d = new (D*)[50];
for(unsigned int i=0; i<50; i++) d[i] = new D; |
Potom nebude vadit virtuální dědičnost, ani další věci, které taky s hodnotovým polem nefungují. |
|
Návrat nahoru |
|
 |
kerekes
Založen: 29. 07. 2007 Příspěvky: 57
|
Zaslal: 5. březen 2008, 21:41:29 Předmět: |
|
|
Jasne uz chapem.
Ja som len povodne myslel ze na to adresovanie pola objektov ma c++ tiez nejaky "polymorfizmus" (myslim tym to, ze si predom zisti skutocny typ objektu, resp velkost pomocou nejakej tabulky/id typu a az potom zaadresuje), ale zjavne nie.
A kedze som tu prvykrat pouzil virtualnu dedicnost tak som nejak defaultne zvalil chybu nato.
Dik za odpovede, problem vyrieseny . |
|
Návrat nahoru |
|
 |
|