WebHU - Programozási kérdések és válaszok

Az alaposztály var tagjára való hivatkozás hozzáféréssértést okoz

Meghatároztam egy származtatott osztály egy var tagját az alaposztály egy var tagjára való hivatkozásként. Ezt azért teszem, mert a származtatott osztályban a hivatkozás neve értelmesebb, mint az eredeti változó neve az alaposztályban.

Most létrehozok egy char puffert, amely elég nagy ahhoz, hogy tartalmazza a származtatott osztály objektumát. Meghatározok egy mutatót a származtatott osztályra, és egy static_cast használatával a pufferre mutatom.

Ha a származtatott osztály egy tagfüggvénye a var alaposztály tagra hivatkozik az alaposztályban meghatározott eredeti nevén, akkor nincs probléma.

De ha a hivatkozás nevével megszüntetik a hivatkozást, memória-hozzáférési szabálysértést kapok.

  1. Miért az eltérő viselkedés?
  2. Hogyan érhetem el azt, amit próbálok, vagyis hogy a származtatott osztályban más néven hivatkozzam a változóra?

    class B {
    public:
        int x;
        B () : x(10) {}
    };
    
    class D : public B {
    public:
        int &y{ x };
        // No problem here:
        inline bool IsXTen () { return ((x == 10) ? true : false); }
        // Memory Access Violation occurs here:
        inline bool IsYTen () { return ((y == 10) ? true : false); }
    };
    
    int main(int argc, char* argv[])
    {
        char buf[sizeof (class D)] = { 0 };
        void *pVoid = buf;
        class D *pd = static_cast<class D*>(pVoid);
    
        if (pd->IsXTen ()) {
            return 1;
        }
    
        if (pd->IsYTen ()) {
            return 2;
        }
    
        return 0;
    }
    

  • Miért static_cast ezt? Elég nagy puffer van egy D számára, de nincs benne D. 15.04.2018
  • A static_cast nem alkot D-t. Mint ilyen, a x és y tagok inicializálása nem garantált a megfelelő osztálydefiníciókban meghatározottak szerint. Ezért bármelyikük elérése meghatározatlan viselkedést okoz. 15.04.2018
  • Ez egy nagyon furcsa módja a dolgoknak. Miért nem csak new D(); a sok karika helyett? Ezenkívül a class D * pd -- a class redundáns. Valószínűleg egyáltalán használnod kell a new-t. 15.04.2018
  • @Clearer Ha nem akarja meghívni a konstruktort (meg is teszi), használja a new D; new D; meghívja a konstruktort. 15.04.2018
  • @tkausl azt mondták, hogy nem. Soha nem használtam. Ha nem hívod a kivitelezőt, az olyan, mintha nem lenne szünet az autódban. 15.04.2018
  • Érdemes megvizsgálni az új elhelyezést. 15.04.2018
  • A puffer már létezik, és egy D osztályú objektumot tartalmaz. Szeretnék erre az objektumra mutatót leadni, hogy D tagfüggvényeket hívhassak meg az adatokon. Ez egy beágyazott rendszerben van, ahol a dinamikus memóriafoglalás (azaz új és törlés) nem engedélyezett. 15.04.2018
  • @sifferman és D osztályú objektumot tartalmaz Az Ön által közzétett kódban nincs D. Ez egy beágyazott rendszerben van, ahol a dinamikus memóriafoglalás (azaz új és törlés) nem engedélyezett. Az új elhelyezés nem foglal le memóriát. 15.04.2018
  • @tkausl Igazad van – az én példámban nem látszik, hogy a puffer D objektummal van feltöltve. Próbáltam rövidre fogni a példát. 15.04.2018
  • @sifferman A hasonlat az, hogy egy autó méretű kartondobozt készítettél, és megpróbálod elindítani és meghajtani. 15.04.2018
  • @sifferman az én példámban nem látható, hogy a puffer egy D objektummal van megtöltve. -- Hány sornyi kód kellett volna ahhoz, hogy a puffer megtelt volna? 15.04.2018
  • Úgy hangzik, hogy placement new megteheti a trükköt. Miért nem hallottam erről korábban? 15.04.2018
  • @sifferman -- A placement-new használata. A cél az objektum tényleges megalkotása -- a kísérleted nem hozott létre objektumot, így nem tudsz objektum dolgokat csinálni rajta, például tagfüggvényeket hívni. Az új elhelyezés lehetővé teszi az objektum építését az Ön által kijelölt memóriaterületen. 15.04.2018
  • Várj egy kicsit... szándékosan létrehozott egy kódpéldát, amelyről tudod, hogy nem működik, mert meghatározatlan viselkedést idéz elő, és választ vársz a nem megjelenített kód összeomlásának okára? 15.04.2018
  • Valójában a fenti kód char puffere minden nullára inicializálva van. Ezért a példa céljaira azt feltételeztem, hogy a int x inicializálva van a 0-re, amit a tesztelés igazolt. Az én tényleges alkalmazásomban a memóriát egy másik folyamat tölti ki. Feltételeztem továbbá, hogy a int&y{x} hatására a y az x álneveként viselkedik, mivel gyakorlatilag minden olvasott hivatkozási magyarázat arra késztet, hogy elhiggyem, így kell tekinteni őket. Valójában azonban a memória-hozzáférés megsértése történik. A placement new mindaddig működnie kell, amíg a konstruktor nem írja felül a memóriában lévő adatokat. 15.04.2018
  • Egyáltalán miért új a tömb és az elhelyezés? int main(){ D d; /*...*/ } mindössze annyit kell tennie, hogy változót hozzon létre a veremben... Ügyeljen arra, hogy ha továbbra is ehhez hasonló elhelyezést használ, kifejezetten meg kell hívnia a destruktort is! 16.04.2018
  • Mellékes megjegyzés: a bármely összehasonlítás eredménye C++-ban már a bool-t írja be! Tehát return condition ? true : false;-re redundáns/elavult kódot állítasz elő, ami egyébként rossz stílus. Ehelyett tegye a return condition;-t... A visszatérési értékek körüli zárójelek szintén rossz stílusúak, és bizonyos esetekben még a viselkedést is megváltoztathatják: a int x = 0; return (x); x-et referenciaként ad vissza! 16.04.2018

Válaszok:


1

A hivatkozás valószínűleg mutatóként van tárolva az objektum memóriaelrendezésében (lásd: Miért foglalják el a hivatkozások a memóriát, ha egy osztály tagjai?). Nem azt a konstruktort hívod meg, amely inicializálja azt a hivatkozást/mutatót, így használata nem definiált.

15.04.2018
  • Köszönjük a választ. Most már értem, hogy miért nem működik, de ez nem oldja meg a problémámat. 16.04.2018
  • Mivel a probléma az, hogy a hivatkozás nincs inicializálva, az egyetlen lehetőség az, hogy inicializálja a hivatkozást, vagy nem használ hivatkozást. 16.04.2018
  • Ha inicializálni szeretné a hivatkozást, hozzon létre egy függvényt, amely ezt teszi, vagy nézze meg, hogyan konstruktor meghívása az alapértelmezett mezők nulla inicializálása nélkül. 16.04.2018
  • Ha nincs szüksége a hivatkozásra, hozzon létre egy gettert. 16.04.2018

  • 2

    Ahogy a megjegyzésekben is említettük, az, ahogyan ezt csinálod, nem építi fel az objektumot. Ebben az esetben használja az „új elhelyezés” kifejezést a megfelelő felépítéshez:

    char buf[sizeof (class D)] = { 0 };
    
    // Class not initialized, constructors not called
    // class D *pd = static_cast<class D*>(pVoid);
    
    // Object is properly initialized
    class D *pd = new (buf) D;
    
    15.04.2018
  • Köszönjük a választ. Kiderült, hogy a placement new nem oldja meg a problémámat, mert az objektum felépítése az adatokat tartalmazó puffer felülírását okozza. Megpróbáltam létrehozni egy objektumot olyan adatok alapján, amelyek már léteztek egy pufferben, anélkül, hogy ezeket az adatokat másoltam volna. 16.04.2018
  • Új anyagok

    A rádiógomb ellenőrzött eseményének használata a jQueryben
    Ebben a cikkben látni fogjuk, hogyan kell dolgozni a jquery választógombbal ellenőrzött eseményeivel. A választógombok HTML gombok, amelyek segítenek kiválasztani egyetlen értéket egy csoportból...

    Körkörös függőségek megoldása terraformban adatforrásokkal – lépésről lépésre
    Mi az a körkörös függőségek Dolgozzunk egy egyszerű eseten, amikor az SQS-sor és az S3-vödör közötti körkörös függőség problémája van egy egymástól függő címkeérték miatt. provider..

    Miért érdemes elkezdeni a kódolást 2023-ban?
    01100011 01101111 01100100 01100101 — beep boop beep boop Világunk folyamatosan fejlődik a technológia körül, és naponta fejlesztenek új technológiákat a valós problémák megoldására. Amint..

    🎙 Random Noise #2  – Örökbefogadás és hit
    az analitika íratlan világának gondozása Szeretné, hogy ezek a frissítések a postaládájába kerüljenek? Iratkozzon fel itt . "Ha önvezető autókat gyártanak, akkor mi miért ne..

    A legrosszabb politika és prediktív modellek májátültetésre jelöltek számára az Egyesült Államokban
    A máj (vagy óangolul lifer) az emberi test legnehezebb belső szervére utal, amely csendesen működik a nap 24 órájában. Mit csinál a máj? 500 feladatot hajt végre a szervezet egészségének..

    5 webhely, amely 2022-ben fejleszti front-end fejlesztői készségeit
    Frontendmentor.io A tényleges projektek létrehozásával a Frontendmentor.io segítséget nyújt a front-end kódolási képességeinek fejlesztésében. A kódolást azután kezdheti meg, hogy..

    Mikor kell használni a Type-t az interfészhez képest a TypeScriptben?
    A TypeScript a JavaScript gépelt szuperkészlete, amely statikus gépelést ad a nyelvhez. Ez megkönnyíti a robusztus és karbantartható kód írását azáltal, hogy a hibákat a fordítási időben..