Sobes.tech
Middle+

Rozprávajte o výzvach pri hádzaní výnimiek (throw exception) v konštruktoroch a deštruktoroch.

sobes.tech AI

Odpoveď od AI

Volanie výnimky v konštruktore môže viesť k úniku zdrojov, ak časť objektov alebo zdrojov vo vnútri konštruktora bola úspešne alokovaná pred vyhodením výnimky, a príslušný destruktor nebude zavolaný. Na prevenciu únikov sa odporúča používať RAII (Resource Acquisition Is Initialization), napríklad pomocou inteligentných ukazovateľov alebo obalových tried, ktoré zabezpečia uvoľnenie zdrojov pri opustení rozsahu.

Volanie výnimky v destruktore vedie k neurčitému správaniu. Ak je destruktor volaný ako dôsledok inej výnimky (stack unwinding), a sám vyvolá neopracovanú výnimku, program sa ukončí volaním std::terminate. Podľa štandardu C++ by mali byť destruktory noexcept.

#include <iostream>
#include <memory> // Pre std::unique_ptr

struct Resource {
    Resource() { std::cout << "Získanie zdroja\n"; }
    ~Resource() { std::cout << "Uvoľnenie zdroja\n"; }
};

struct BadConstructor {
    Resource res; // Zdroj bude alokovaný pred vyhodením výnimky
    BadConstructor() {
        std::cout << "Konštruktor BadConstructor\n";
        throw std::runtime_error("Chyba v konštruktore"); // Vyhodenie výnimky
        // Destruktor Resource nebude volaný, ak je výnimka vyhodená pred dokončením konštruktora
    }
    ~BadConstructor() noexcept { // Destruktory by mali byť noexcept
        std::cout << "Destruktor BadConstructor\n";
    }
};

struct GoodConstructor {
    std::unique_ptr<Resource> res; // Použitie inteligentného ukazovateľa pre RAII
    GoodConstructor() : res(std::make_unique<Resource>()) {
        std::cout << "Konštruktor GoodConstructor\n";
        // Ak je vyhodená výnimka, unique_ptr zabezpečí volanie destruktora Resource pri opustení rozsahu
        // throw std::runtime_error("Chyba v konštruktore");
    }
    ~GoodConstructor() noexcept {
        std::cout << "Destruktor GoodConstructor\n";
    }
};

struct BadDestructor {
    BadDestructor() { std::cout << "Konštruktor BadDestructor\n"; }
    ~BadDestructor() {
        std::cout << "Destruktor BadDestructor\n";
        // Nie je vhodné vyhadzovať výnimku z destruktora, najmä počas unwind
        // throw std::runtime_error("Chyba v destruktore");
    }
};

int main() {
    // Príklad s konštruktorom (vedie k úniku zdrojov bez RAII)
    try {
        // BadConstructor bc; // Odkomentovať pre demonštráciu úniku
    } catch (const std::exception& e) {
        std::cout << "Chytená výnimka: " << e.what() << "\n";
    }

    // Príklad s dobrým konštruktorom (používa RAII)
    try {
        // GoodConstructor gc; // Odkomentovať pre demonštráciu, žiadny únik nebude
    } catch (const std::exception& e) {
        std::cout << "Chytená výnimka: " << e.what() << "\n";
    }

    // Príklad s destruktorom (môže viesť k std::terminate)
    try {
        // BadDestructor bd; // Odkomentovať, ak chcete vidieť potenciálny terminate
        // throw std::runtime_error("Ďalšia výnimka"); // spôsobí unwind
    } catch (const std::exception& e) {
        std::cout << "Chytená výnimka: " << e.what() << "\n";
    }

    return 0;
}

Tabuľka:

Miesto volania výnimky Potenciálne problémy Odporúčané riešenie
Konštruktor Únik zdrojov Používať RAII (inteligentné ukazovatele, obalové triedy)
Destruktor Nedefinované správanie, std::terminate pri unwind Destruktory musia byť noexcept, vyhýbať sa vyhadzovaniu výnimiek