C++ a ukazatel na objekt

asd

C++ a ukazatel na objekt
« kdy: 19. 05. 2011, 15:49:33 »
Caute,

ako by som mal nasledujuci kod upravit aby fungoval?

Kód: [Vybrat]
class foo
{
    public:
        void a() {}
};

class bar : public foo
{
    public:
        void b() {}
};

int main()
{
    foo * obj = new bar;
    obj->a(); // ok
    obj->b(); // error: 'class foo' has no member named 'b'

    delete obj;

    return 0;
}

cize co vlastne chcem spravit: mam premennu typu foo* do ktorej ulozim adresu na dynamicky alokovanu pamat triedy bar a chcem na tomto objekte pouzit funkciu b() ktora je definovana v triede bar ktora dedi od triedy foo.

Dakujem
« Poslední změna: 19. 05. 2011, 17:35:28 od Petr Krčmář »


danielsoft

Re: c++ ukazovatel na objekt
« Odpověď #1 kdy: 19. 05. 2011, 15:55:36 »
objektove nejcistsi by asi bylo v tride A nadefinovat virtualni funkci b(). objekt pak pomoci tabulky virtualnich metod dynamicky zavola potrebnou metodu podle toho, na jakou tridu odkaz bude

asd

Re: c++ ukazovatel na objekt
« Odpověď #2 kdy: 19. 05. 2011, 16:01:06 »
danielsoft: ano, ale zabudol som povedat ze ono to mal byt kod (teda samozrejme nie tento :D) 3rd party kniznice a do toho sa mi nechce hrabat (keby ze tu kniznicu chcem updatovat tak zasa by som to musel prepisovat atd atd...)

tak som to vyriesil takto "spinavsie":

Kód: [Vybrat]
bar * obj2 = static_cast<bar*>(obj);
obj2->b();

Re: c++ ukazovatel na objekt
« Odpověď #3 kdy: 19. 05. 2011, 16:43:16 »
static_cast je v tomto případě asi nejčištší workaround. Dá se použít v C++, pokud potomek nemá žádné členské proměnné ani virtuální funkce...  Tedy ono se to dá použít i tak, ale volaná funkce nesmí pracovat s membery deklarované v potomkovi a při volání virtuálních metod nesmí člověk být překvapen, že se zavolá předek. Virtuální metody nově deklarované v potomkovi se volat nesmí.

Používám tenhle hack, pokud se potřebuju z nějakých důvodů dostat k proměnným deklarovaným "protected:" u knihoven třetích stran. Sám ve všech třídách používám výhradně deklaraci "protected:" abych si tuhle cestu rozšiřování v budoucnosti neuzavřel.

Re: C++ a ukazatel na objekt
« Odpověď #4 kdy: 19. 05. 2011, 23:12:31 »
V tomto případě bych použil dynamic_cast - ten je přesně na toto dělaný. Za běhu zkontroluje typ (je tedy o trochu pomalejší než static_cast) a pokud by neseděl (tj. jeden objekt by nebyl potomkem druhého), vrátí NULL.


telra

Re: C++ a ukazatel na objekt
« Odpověď #5 kdy: 20. 05. 2011, 08:27:24 »
Mozes prosim priblizit blizsie ten problem? Celkom ma to zaujalo, najma preto ze si neviem predstavit normalnu situaciu kde nieco taketo nastane.....

Re: c++ ukazovatel na objekt
« Odpověď #6 kdy: 20. 05. 2011, 09:13:43 »
danielsoft: ano, ale zabudol som povedat ze ono to mal byt kod (teda samozrejme nie tento :D) 3rd party kniznice a do toho sa mi nechce hrabat (keby ze tu kniznicu chcem updatovat tak zasa by som to musel prepisovat atd atd...)

tak som to vyriesil takto "spinavsie":

Kód: [Vybrat]
bar * obj2 = static_cast<bar*>(obj);
obj2->b();
Proč tak složitě? Stačí
Kód: [Vybrat]
((bar*)(obj))->b();

Logik

  • *****
  • 1 031
    • Zobrazit profil
    • E-mail
Re: C++ a ukazatel na objekt
« Odpověď #7 kdy: 20. 05. 2011, 10:45:52 »
static_cast je bezpečnější. Nepovede se Ti omylem něco přetypovat mimo hierarchii. Zdá se, že jde o blbinu, ale stačí jedna taková chyba a půl dne ladění a člověk píše všude radši static_cast... :-)

Re: c++ ukazovatel na objekt
« Odpověď #8 kdy: 20. 05. 2011, 12:47:30 »
Proč tak složitě? Stačí
Kód: [Vybrat]
((bar*)(obj))->b();

Protože to není totéž. static_cast umí zařídit i patřičný offset u vícenásobné dědičnosti:


Citace: Aleš Janda
V tomto případě bych použil dynamic_cast - ten je přesně na toto dělaný. Za běhu zkontroluje typ (je tedy o trochu pomalejší než static_cast) a pokud by neseděl (tj. jeden objekt by nebyl potomkem druhého), vrátí NULL.

dynamic_cast je dobré používat tam, kde opravdu nemám jistotu, že to co mi přilezlo ukazatelem je skutečně objekt, který očekávám. dynamic_cast má tyto nevýhody
  • Lze použít pouze na objekty, které mají nějaké virtuální funkce (stačí destruktor)
  • Nelze jej použít jako reinterpret_cast, tedy objekt opravdu musí být tím, co očekáváme (někdy se hodí umět to přetypovat navzdory)
  • Vyžaduje akci v runtime. Zatímco všechny XXX_cast operátory vyhodnocuje překladač, dynamic_cast se vyhodnocuje za běhu. A není to zrovna levná záležitost.

Většinou se to řeší tak, že v tabulce virtuálních metod je přibalen ukazatel na záznam o třídě objektu a ten obsahuje odkaz na tabulku všech tříd, které dědí a to i tranzitivně. Operátor pak projde tabulku a nalezne v ní třídu ukazatele a cílovou třídu a spočítá transformaci výsledného pointeru... tedy spočítá jeho offset. Jako klíč do tabulky bývá zakódované jméno třídy (porovnávají se řetězce) - Microsoft, případně nějaké GUID třídy (COM+, prý to používá i gcc)

Nicméně, pokud vím, že mi do nějaké funkce může přilézt objekt jen určitého potomka k danému rozhraní, nebo základní třídy, tak používám static_cast, je to rychlejší. Je však dobré dynamic_cast ověřit aspoň pomocí _assertu v testovací verzi.

PS: Nevýhoda static_castu: nedá se použít na virtuální dědičnost.

Logik

  • *****
  • 1 031
    • Zobrazit profil
    • E-mail
Re: C++ a ukazatel na objekt
« Odpověď #9 kdy: 20. 05. 2011, 13:17:26 »
Ondra: s tim C like castem je to IMHO jinak. Co vím, tak funguje jako "best cast": pokud jde static cast, udělá static cast, pokud nejde, udělá reinterpret cast. Takže offsety IMHO počítá také. Pokud bych chtěl bez počítání, musím to vzít reinterpretem, nebo C-like castem přes mezityp void.

http://www.cplusplus.com/forum/general/19300/

Nebo se překladače nechovaj dle normy?

Re: C++ a ukazatel na objekt
« Odpověď #10 kdy: 20. 05. 2011, 13:40:43 »

Nebo se překladače nechovaj dle normy?

Nevím, upřímě normu neznám zpaměti a doposud jsem C style cast na pointer považoval automaticky za reinterpret_cast

Re: C++ a ukazatel na objekt
« Odpověď #11 kdy: 20. 05. 2011, 13:44:50 »
Mimochodem, nevíte někdo náhodou, jestli C style cast na (void *) u polymorfních objektů uděla dynamic_cast<void *>()

Nebo bych potřeboval nějaký univerzální castovač na void *. Hodí se to, pokud chce člověk zjistit base adresu objektu. U polymorfních objektů lze právě použít dynamic_cast<void *> ale pokud se nejedná o polymorfní objekt, tak to selže na chybě překladu. Dosti komplikovaným způsobem přes několik desítek šablon lze udělat to, že poznám primitivní typ, poznam polymorfní typ a na základě toho zvolím castovač, ale je to opruz.

Logik

  • *****
  • 1 031
    • Zobrazit profil
    • E-mail
Re: C++ a ukazatel na objekt
« Odpověď #12 kdy: 20. 05. 2011, 14:01:03 »
cast na void * myslím nic nepočítá.

Ale jestli to umíš udělat, tak to zabal do nějaký funkce a že to v tý knihovně šíli Ti může bejt jedno, ne? Nicméně tu šílenost bych rád viděl :-).

Re: C++ a ukazatel na objekt
« Odpověď #13 kdy: 20. 05. 2011, 16:04:39 »
cast na void * myslím nic nepočítá.

Ale jestli to umíš udělat, tak to zabal do nějaký funkce a že to v tý knihovně šíli Ti může bejt jedno, ne? Nicméně tu šílenost bych rád viděl :-).

No to není věc knihovny, ale hlavičkových souborů. A to je horší. A v zásadě to vypadá tak, že se deklaruje šablona IsPrimitiveType<T>, které se lze zeptat, zda T je primitivní typ (třeba int, short), pak je tam šablona IsPointer<T> a IsReference<T>. Jakmile se vyloučí tyhle typy, pak se řeší, zda je IsPolymorphic<T>, ten se dělá tak, že se původní typ podědí a přidá se mu virtuální destruktor a měří se velikost výsledného objektu. Pokud se liší od objektu původního typu, znamená to, že původní typ nebyl polymorfní (přidáním virtuálního destruktoru se založila tabulka virtuálních adres). No a na konci téhle anabáze lze rozdělit cast na void na případ, kdy se udělá reinterpret_cast a dynamic_cast. Z toho jsem schopen zjistit adresu objektu, a offset mezi dodaným ukazatelem a skutečným začátkem objektu v paměti.

Vyhodnocení dělá překladač, ve výsledném kódu je pro polymorfní typy dynamic_cast a pro nepolymorfní typy nic (cast se nepřekládá). Vyhodnocení všech šablon dost protahuje překlad kódu.

dante

Re: C++ a ukazatel na objekt
« Odpověď #14 kdy: 20. 05. 2011, 16:54:02 »
Ondra: s tim C like castem je to IMHO jinak. Co vím, tak funguje jako "best cast": pokud jde static cast, udělá static cast, pokud nejde, udělá reinterpret cast. Takže offsety IMHO počítá také. Pokud bych chtěl bez počítání, musím to vzít reinterpretem, nebo C-like castem přes mezityp void.

http://www.cplusplus.com/forum/general/19300/

Nebo se překladače nechovaj dle normy?

problem s C-style pretypovanim je inde...kratky priklad

struct a {
...
};

struct b; //< forward declaration

main(...)
{
   b* v0=(b*)new a(); // compiler prejde bez problemov...prijemnu zabavu s hladanim problemu

   b* v1=static_cast<b*>(new a()); // compiler zahlasi chybu... 'static_cast' : cannot convert from 'a *' to 'b *'"
}

vysvetlovat preco si nechat C-style pretypovanie pre C asi uz nema vyznam...