Sobes.tech
Middle+

Meséljen az exception dobás kihívásairól (throw exception) a konstruktorokban és destruktorokban.

sobes.tech MI

Válasz az MI-től

A kivétel hívása a konstruktorban erőforrás-szivárgáshoz vezethet, ha a objektumok vagy erőforrások egy része sikeresen lefoglalásra került a kivétel dobása előtt, és a megfelelő destruktor nem hívódik meg. Az szivárgások elkerülése érdekében ajánlott az RAII (Resource Acquisition Is Initialization) használata, például okos mutatók vagy csomagoló osztályok segítségével, amelyek garantálják az erőforrások felszabadítását a hatókörből való kilépéskor.

A destruktorban dobott kivétel viselkedése nem meghatározott. Ha a destruktor egy másik kivétel eredményeként hívódik meg (stack unwinding), és maga is kivételt dob, a program std::terminate-tel végződik. A C++ szabvány szerint a destruktoroknak noexcept-nak kell lenniük.

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

struct Resource {
    Resource() { std::cout << "Erőforrás megszerzése\n"; }
    ~Resource() { std::cout << "Erőforrás felszabadítva\n"; }
};

struct BadConstructor {
    Resource res; // Erőforrás a kivétel dobása előtt foglalva
    BadConstructor() {
        std::cout << "BadConstructor konstruktor\n";
        throw std::runtime_error("Hiba a konstruktorban"); // Kivétel dobása
        // A Resource destruktor nem hívódik meg, ha a kivétel a konstruktor végén dobódik
    }
    ~BadConstructor() noexcept { // A destruktoroknak noexcept-nek kell lenniük
        std::cout << "BadConstructor destruktor\n";
    }
};

struct GoodConstructor {
    std::unique_ptr<Resource> res; // Okos mutató RAII-hoz
    GoodConstructor() : res(std::make_unique<Resource>()) {
        std::cout << "GoodConstructor konstruktor\n";
        // Ha kivétel dobódik, a unique_ptr garantálja a Resource destruktorának hívását a hatókörből való kilépéskor
        // throw std::runtime_error("Hiba a konstruktorban");
    }
    ~GoodConstructor() noexcept {
        std::cout << "GoodConstructor destruktor\n";
    }
};

struct BadDestructor {
    BadDestructor() { std::cout << "BadDestructor konstruktor\n"; }
    ~BadDestructor() {
        std::cout << "BadDestructor destruktor\n";
        // Nem szabad kivételt dobni a destruktorból, különösen unwind közben
        // throw std::runtime_error("Hiba a destruktorban");
    }
};

int main() {
    // Példa a konstruktorra (erőforrás szivárgáshoz vezethet RAII nélkül)
    try {
        // BadConstructor bc; // Kikommentelve a szivárgás demonstrálásához
    } catch (const std::exception& e) {
        std::cout << "Kivétel elkapva: " << e.what() << "\n";
    }

    // Példa a jó konstruktorra (RAII-t használ)
    try {
        // GoodConstructor gc; // Kikommentelve, nem lesz szivárgás
    } catch (const std::exception& e) {
        std::cout << "Kivétel elkapva: " << e.what() << "\n";
    }

    // Példa a destruktorra (akár std::terminate-hez vezethet)
    try {
        // BadDestructor bd; // Kikommentelve, ha a potenciális terminate-et akarod látni
        // throw std::runtime_error("Másik kivétel"); // unwind-et okoz
    } catch (const std::exception& e) {
        std::cout << "Kivétel elkapva: " << e.what() << "\n";
    }

    return 0;
}

Táblázat:

Kivétel hívás helye Potenciális problémák Ajánlott megoldás
Konstruktor Erőforrás szivárgás RAII használata (okos mutatók, csomagoló osztályok)
Destruktor Nem meghatározott viselkedés, std::terminate unwind közben A destruktoroknak noexcept-nek kell lenniük, kerülje a kivétel dobását