C++ polymorfizmus

Rurik

C++ polymorfizmus
« kdy: 29. 07. 2018, 16:58:38 »

class A {
private:
    int a;
public:
    A() : a{10} {}
    virtual void f(ostream& str) {
        str << a;
    }
};
class B : public A {
private:
    int a;
public:
    B() : a{500} {}
    void f(ostream &str) {
        str << a;
    }
};

int main()
{
    std::unique_ptr<A> a = std::make_unique<A>();
    std::unique_ptr<B> b = std::make_unique<B>();
    std::unique_ptr<A> a1 = std::make_unique<B>();
    a->f(cout); // 10 OK
    cout << endl;
    b->f(cout); // 500 OK
    cout << endl;
    a1->f(cout); // 500 OK
    cout << endl;

    A _a;
    B _b;
    A _a1 = B();
    _a.f(cout); // 10 OK
    cout << endl;
    _b.f(cout); // 500 OK
    cout << endl;
    _a1.f(cout); // 10 WTF?!!!!
    cout << endl;
    return 0;
}

Dokaze mi niekto vysvetlit toto odlisne chovanie pri premennych alokovanych na zasobniku? Pride mi to neuveritelne neintuitivne, nelogicke a takmer by som povedal, ze to je fatalna chyba navrhu C++. Nedokazem si momentalne predstavit aky to ma dovod, ale snad mi to niekto dokaze vysvetlit.


v

Re:C++ polymorfizmus
« Odpověď #1 kdy: 29. 07. 2018, 17:26:41 »
asi kvůli tomu jak funguje alokace na zásobníku viz https://stackoverflow.com/questions/5894775/polymorphic-objects-on-the-stack

lopata

Re:C++ polymorfizmus
« Odpověď #2 kdy: 29. 07. 2018, 17:29:13 »
Protože proměnná a je ve třídě B 2x. Poprvé zděděná z A a inicializovaná na 10 a podruhé z B inicializovaná na 500. Potom když do A přiřadíš B, tak je logické, že se použije proměnná z A.

Ooo

Re:C++ polymorfizmus
« Odpověď #3 kdy: 29. 07. 2018, 17:31:02 »
Polymorfismus funguje pro ukazatele a reference.

JSH

Re:C++ polymorfizmus
« Odpověď #4 kdy: 29. 07. 2018, 17:31:34 »
Doporučuju přidat výpisy do konstruktorů (včetně kopírovacích) a destruktorů. Problém je tu :
Kód: [Vybrat]
A _a1 = B();- Vytvoří se dočasná proměnná typu B
- Pomocí téhle dočasné proměnné se inicializuje proměnná _a1 typu A (tzn, zkopíruje se kus té dočasné proměnné)
- Ta dočasná proměnná typu B se zruší.

Není to chyba návrhu C++. Je to proto, že zásobník se chová jinak než halda. Na zásobníku jsou proměnné přímo. Nejsou schované za ukazatelem, který by mohl být jiného typu.


hu

Re:C++ polymorfizmus
« Odpověď #5 kdy: 29. 07. 2018, 17:35:46 »
Javisti, boze boze...

Ooo

Re:C++ polymorfizmus
« Odpověď #6 kdy: 29. 07. 2018, 17:40:37 »
Javisti, boze boze...
Co to je za odpověď?!!!

DogWithFleas

Re:C++ polymorfizmus
« Odpověď #7 kdy: 29. 07. 2018, 17:50:24 »
Object splicing. Jak pise JSH, chyba je v tom jednom radku -- jelikoz to neni polymorfni prirazeni, ale pretypovani.

Polymorfni objekty na stacku mit muzu, pokud pouzivam pointery/reference.
Kód: [Vybrat]
...
A * _a2 = &_b;
_a2->f (cout); // 500
cout << endl;

cout << "The type of _a: " << typeid(_a).name () << endl; // class A
cout << "The type of _b: " << typeid(_b).name () << endl; // class B
cout << "The type of _a1: " << typeid(_a1).name () << endl; // class A
cout << "The type of _a2: " << typeid(*_a2).name () << endl; // class B

Rurik

Re:C++ polymorfizmus
« Odpověď #8 kdy: 29. 07. 2018, 17:55:26 »
Doporučuju přidat výpisy do konstruktorů (včetně kopírovacích) a destruktorů. Problém je tu :
Kód: [Vybrat]
A _a1 = B();- Vytvoří se dočasná proměnná typu B
- Pomocí téhle dočasné proměnné se inicializuje proměnná _a1 typu A (tzn, zkopíruje se kus té dočasné proměnné)
- Ta dočasná proměnná typu B se zruší.

Není to chyba návrhu C++. Je to proto, že zásobník se chová jinak než halda. Na zásobníku jsou proměnné přímo. Nejsou schované za ukazatelem, který by mohl být jiného typu.
Vdaka za vysvetlenie!

.

Re:C++ polymorfizmus
« Odpověď #9 kdy: 29. 07. 2018, 17:57:04 »
Nesouvisí to s alokací na zásobníku a není to odlišné chování. Je to úplně jiná věc v tom, co a kam přiřazuješ. Třída je pořád jen struktura, holt jde takhle přiřadit a to a1 nemá ani ponětí o B. Samozřejmě z toho vyplývá, že se i oříznou data navíc a to už může být opravdová zadek. V céčku trochu musíš vědět, co děláš.

Franta <xkucf03/>

Re:C++ polymorfizmus
« Odpověď #10 kdy: 29. 07. 2018, 18:35:15 »
Taky jsem si tímhle WTF momentem prošel :-)

Podívej se na Object slicing. A zamysli se nad tím, kdy se objekt kopíruje a když se přiřazuje ukazatel/reference na tu samou instanci. Vyzkoušej si to v debuggeru, napiš si pár příkladů… Funguje to prostě jinak než Java, i když syntaxe je na první pohled podobná, a taky to někdy funguje neintuitivně, pokud neznáš jak to funguje uvnitř. Většina lidí s tím má problémy, ale za vším je nějaký více či méně dobrý důvod (v horším případě historický důvod).

Rurik

Re:C++ polymorfizmus
« Odpověď #11 kdy: 30. 07. 2018, 10:09:28 »
Taky jsem si tímhle WTF momentem prošel :-)

Podívej se na Object slicing. A zamysli se nad tím, kdy se objekt kopíruje a když se přiřazuje ukazatel/reference na tu samou instanci. Vyzkoušej si to v debuggeru, napiš si pár příkladů… Funguje to prostě jinak než Java, i když syntaxe je na první pohled podobná, a taky to někdy funguje neintuitivně, pokud neznáš jak to funguje uvnitř. Většina lidí s tím má problémy, ale za vším je nějaký více či méně dobrý důvod (v horším případě historický důvod).
Snad uz tomu rozumiem. Ja viem, ze ak mam hierarchiu A -> B -> C tak ak vytvorim instanciu objektu C ten bude v pamati zahrnovat aj podobjekty A a B. Akurat som si neuvedomil, ze ak urobim:
Kód: [Vybrat]
A a = B();
tak to co sa realne v C++ stane je, ze sa spravi kopia a objekt B sa odstrani, do premennej A sa vlozi len ten podobjekt A. Zaujimave je, ze sa to da riesit aj takto:
Kód: [Vybrat]
B b;
A& _a = b;
potom to funguje tak ako ocakavam.

.

Re:C++ polymorfizmus
« Odpověď #12 kdy: 30. 07. 2018, 11:15:59 »
Protože reference je technicky zase jen ukazatel.
Kód: [Vybrat]
A& _a = b;
A* _a = &b;
Takže ke slicingu nedojde.