Problémy C++

Problémy jsou vždy. A tady je něco málo + řešení. Pokud máš nějaký zajímavý problém, tak se ozvi. Pokud bude zajímavý, rád ho zveřejním .. kde ? no přece tady.

Seznam problémů:

  • C-Builder verze 3
  • Pozor C-Builder verze 3 neuvolňuje vyjimky ....
  • C-Builder - obecně
  • Pozor na dědění VCL-kových (pascallovských) vyjímek
  • Jednotlivé překladače
  • Záludnosti u allokace paměti pomocí "new"

  • Pozor C-Builder verze 3 neuvolňuje vyjímky, přesněji řečeno nevolá se destruktor kopie odchycené vyjímky. ALE JEN v případě že máš zaplou podporu knihovny "VCL" - a to máš zapnuté vždy pokud děláš windowsové aplikace, protože většina komponent je součástí této knihovny.


    Při vzniku a odchycení vyjímky by správně měli následovat tyto akce:
    1. Konstruktor vyjímky
    2. Po vstupu do bloku "catch" - Kopírovací konstruktor vyjímky (s tímto objektem budeme pracovat)
    3. Destruktor vlastní vyjímky
    4. Po ukončení bloku "catch" - destruktor kopie vyjímky
    Ale pokud si ztáhneš tento soubor, vytvoříš si konzolový projekt se zaškrlou volbou "Include the Visual Component Library (VCL)" a vložíš do něj kód ze souboru, tak po spustění by jsi měl dostat výsledek:

    Call exception
    CONSTRUCTOR of ORGINAL
    CONSTRUCTOR of COPY
    DESTRUCTOR of ORGINAL
    DESTRUCTOR of COPY

    Ale po kompilaci v C-Builderu verze 3 se zaplou podporou VCL dostaneš výsledek:

    Call exception
    CONSTRUCTOR of ORGINAL
    CONSTRUCTOR of COPY
    DESTRUCTOR of ORGINAL

    Jak je vidět jedno volání destruktoru chybí (Bod 4 se neprovedl). A kdybys ještě odznačil volbu "Projekt->Options->C++" "Exception Handling" "Destructor Cleanup", tak se ti nezavola ani jeden destruktor.

    A kdybys použil zápis "catch(...)" pro odchycení všech vyjímek, jak se často ikdyž ne moc chytře, ale pohodlně používá, tak nemáš ani jak tuto paměť uvolnit.

    Závěr:
    Pokud používáš C-Builder verzi 3 musíš si vyjímku uvolnit sám (na konci bloku "catch"). Pokud chceš kód přenášet doporučuji toto uvolnění dát do bloku podmíněčného překladu. POZOR toto uvolnění nesmí být použito v C-Builder verze 4.

    #define CBUILDER_VER_3

    //in catch blok
    #ifdef CBUILDER_VER_3
      delete e&;
    #endif

    Ve zdrojovém souboru je tato konstrukce použitá.
    Pokud si chceš stáhnout malý test tak zde je zkomprimovany exe soubor a zde je zkomprimovaný projekt v C-Builderu verze 3. V testu je vyvolávána vyjímka a odchytávána. Pokud zaškrkneš volbu pro volaní delete (toto volání je vloženo na konci "catch" bloku), tak bude vše OK, ale pokud ne, tak ti pomalu ale jistě bude ubývat paměť.

    Zpět na seznam problémů


    Pozor na dědění VCL-kových (pascallovských) vyjímek

    Pokud si z třídy Exceptions (nebo z jiné odvozené z této třídy), která je definovaná v pascallu, odvodíš jinou vyjímkovou třídu a po té tuto vyjímku zavoláš a odchytíš, TAK:
    1. Se zavolá konstruktor vyjímky
    2. Po ukončení bloku "catch" se vyjimka uvolní (v C-Builderu verze 3 ne)
    Správně (dle normy C++) by se při odchycení měla vytvořit kopie vyjímkového objektu pomocí kopírovacího konstruktoru. S tímto zkopírovaným objektem potom pracujeme v bloku "catch".

    Ale pokud si ztáhneš tento soubor, vytvoříš si konzolový projekt se zaškrlou volbou "Include the Visual Component Library (VCL)" a vložíš do něj kód ze souboru, tak po spustění by jsi měl dle pravidel C++ dostat výsledek:

    Call exception
    CONSTRUCTOR of ORGINAL
    CONSTRUCTOR of COPY
    DESTRUCTOR of ORGINAL
    DESTRUCTOR of COPY

    Ale po kompilaci dostaneš výsledek:

    Call exception
    CONSTRUCTOR of ORGINAL
    DESTRUCTOR of ORGINAL

    Závěr:
    Nevolá se kopírovací konstruktor, proto pokud děláš něco jiného v konstruktoru a v kopírovacím konstruktoru, tak máš smůlu, protože kopírovací konstruktor se nikdy nezavolá.
    S uvolněním vyjímky pod C-Builderem verze 3 platí také problém předchozí (vyjímka se neuvolní, musíš sám).

    Zpět na seznam problémů


    Záludnosti u allokace paměti pomocí operátoru "new"

    U operatoru "new", sloužící pro alokaci "hrubé paměti" nebo allokaci pamětí pro objekty je jeden zásadní problém a to jak ošetřit uspěšnou alokaci. Teď si asi říkáš co je na tom - vysvětlím.
    Pokud víš jaký překladač používáš a jak funguje a víš jak jsou psány všechny zdrojové soubory , tak není problém. Problém nastane až když chceš kód někam přenést nebo přejdeš třeba z C-Builderu verze 4 na verzi 5.
    Záludnost je v tom, že co překladač to jiný způsob "oznámení" o neúspěchu alokace paměti. Dle normy ANSI/ISO by se měla při neúspěšné alokaci vyvolat vyjímka "bad_alloc". Pokud si byl zvyklý na nějaký staší překladač (např. BC ver.3), tak máš smůlu protože tam se vyjímka nevyvolala ale ukazatel byl nastaven na 0 (NULL). Je tady možnost použití funkce "set_new_handler", která umožňuje přestavení chování "new" při neúspěšné alokaci. A jako vždy je tady ALE - set_new_handler(0) by dle normy měl vrátit(nastavit) vyvolání vyjímky "bad_alloc", ale dokonce i v helpu od C-Builderu verze 4 se dočtete něco jiného.
    Přeložil jsem stejný kód, který se pokuší alokovat 2Gb paměti (doufám že tolik nemáš) na různých překladačích a tady jsou výsledky:
  • C-Builder verze 3
    - podporuje "new(nothrow)" nevyvolána vyjímku a ukazatel je nastaven na 0 (NULL)... dle normy (tady se mně nepodařilo alokovat pole int, ale já tento nástroj nepoužívám, tak jsem se s tím netrápil)
    - "new" vyvolá vyjímku "bad_alloc"... dle normy
    - set_new_handler(0) nastaví chování "new" bez vyjímky a nastavení ukazatele na 0 (NULL)... nesouhlasí z normou
  • C-Builder verze 4
    - podporuje "new(nothrow)" nevyvolána vyjímku a ukazatel je nastaven na 0 (NULL)... dle normy
    - "new" vyvolá vyjímku "bad_alloc"... dle normy
    - set_new_handler(0) nastaví chování "new" bez vyjímky a nastavení ukazatele na 0 (NULL)... nesouhlasí z normou
  • Borland C++ Compiler 5.5
    - podporuje "new(nothrow)" nevyvolána vyjímku a ukazatel je nastaven an 0 (NULL)... dle normy
    - "new" vyvolá vyjímku "bad_alloc"... dle normy
    - set_new_handler(0) nastaví chování "new" na vyvolání vyjímky "bad_alloc"... dle normy
  • Microsoft Visual Studio verze 6.0
    - podporuje "new(nothrow)" nevyvolána vyjímku a ukazatel je nastaven an 0 (NULL)
    - "new" nevyvolá vyjímku a ukazatel je nastaven an 0 (NULL)... nesouhlasí z normou
    - set_new_handler(0) nastaví chování "new" na nevyvolání vyjímky a mám podezření, že ve skutečnosti nedělá vůbec nic... nesouhlasí z normou
  • Linux GCC - RedHat verze 6.0
    - Tady to dopadlo bídňe. Nebyla volána vyjímka a ani nebyl ukazatel nastaven na "0" - nikdy. Jen se vytvořilo "core" (Zkoušel jsem to na dvou různých instalacích)
  • Linux GCC - RedHat verze 6.2
    - podporuje "new(nothrow)" nevyvolána vyjímku a ukazatel je nastaven an 0 (NULL)... dle normy
    - "new" vyvolá vyjímku "bad_alloc"... dle normy
    - set_new_handler(0) nastaví chování "new" na vyvolání vyjímky "bad_alloc"... dle normy
  • UNIX (SGI Origin) Compilator ver 7.3
    - podporuje "new(nothrow)" nevyvolána vyjímku a ukazatel je nastaven an 0 (NULL)... dle normy
    - "new" vyvolá vyjímku "bad_alloc"... dle normy
    - set_new_handler(0) nastaví chování "new" na vyvolání vyjímky "bad_alloc"... dle normy
  • Závěr:
    Dle normy se z "windowsových" překladačů chová jen "Borland C++ Compiler 5.5".
    Máš asi co upravovat pokud využíváš nějaké kódy napsané dříve, či chceš mít nějaký kód přenositelný. Samozřejmě máš možnost použít operátor "new(nothrow)" a ošetřovat alokaci paměti na návrat 0 (NULL) - to by mělo platit vždy.

    //možná budeš potřebovat tento řádek (např. C-Builder ver.4)
    const std::nothrow_t std::nothrow;

    TMyClass pointer;

    if((pointer = new(nothrow) TMyClass()) == 0)
    {
     //reakce na neúspěšnou alokaci
    }

    Ale stejně tak se musíš přesvědčit jestli to tvůj překladač podporuje.

    Já si myslím, že každá dynamická alokace paměti byť sebemenší, by měla být ošetřena. Ale jak to zařídit, aby bylo možné programu přenášet z překladače na překladač to nevím. Doufejme, že pokud se budem držet normy, tak nás tvůrci překladačů (i od microsoftů) mile překvapí a budou se jí držet taky. Ale buďme klidní, oni určitě zase přijdou z jiným rozporem.

    Zpět na seznam problémů


    Sekce develop na imega.cz

    NAVRCHOLU.cz