.[ ČeskéHry.cz ].
Problem s virtualnou dedicnostou

 
odeslat nové téma   Odpovědět na téma    Obsah fóra České-Hry.cz -> C / C++
Zobrazit předchozí téma :: Zobrazit následující téma  
Autor Zpráva
kerekes



Založen: 29. 07. 2007
Příspěvky: 57

PříspěvekZaslal: 2. březen 2008, 19:40:55    Předmět: Problem s virtualnou dedicnostou Odpovědět s citátem

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
Zobrazit informace o autorovi Odeslat soukromou zprávu
Marek



Založen: 28. 07. 2007
Příspěvky: 1782
Bydliště: Velká Morava

PříspěvekZaslal: 2. březen 2008, 21:27:31    Předmět: Odpovědět s citátem

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
Zobrazit informace o autorovi Odeslat soukromou zprávu
kerekes



Založen: 29. 07. 2007
Příspěvky: 57

PříspěvekZaslal: 2. březen 2008, 23:06:16    Předmět: Odpovědět s citátem

Dik, nejak to predesignujem.
Návrat nahoru
Zobrazit informace o autorovi Odeslat soukromou zprávu
Al



Založen: 23. 10. 2007
Příspěvky: 196

PříspěvekZaslal: 5. březen 2008, 09:57:18    Předmět: Odpovědět s citátem

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
Zobrazit informace o autorovi Odeslat soukromou zprávu
kerekes



Založen: 29. 07. 2007
Příspěvky: 57

PříspěvekZaslal: 5. březen 2008, 21:41:29    Předmět: Odpovědět s citátem

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 Smile.
Návrat nahoru
Zobrazit informace o autorovi Odeslat soukromou zprávu
Zobrazit příspěvky z předchozích:   
odeslat nové téma   Odpovědět na téma    Obsah fóra České-Hry.cz -> C / C++ Časy uváděny v GMT + 1 hodina
Strana 1 z 1

 
Přejdi na:  
Nemůžete odesílat nové téma do tohoto fóra
Nemůžete odpovídat na témata v tomto fóru
Nemůžete upravovat své příspěvky v tomto fóru
Nemůžete mazat své příspěvky v tomto fóru
Nemůžete hlasovat v tomto fóru


Powered by phpBB © 2001, 2005 phpBB Group


Vzhled udelal powermac
Styl "vykraden" z phpBB stylu MonkiDream - upraveno by rezna