Sobes.tech
Middle+

Vorbește despre provocările aruncării excepțiilor (throw exception) în constructori și destructori.

sobes.tech AI

Răspuns de la AI

Apelarea unei excepții în constructor poate duce la scurgeri de resurse dacă o parte a obiectelor sau resurselor din interiorul constructorului au fost alocate cu succes înainte de momentul aruncării excepției, iar destructorul corespunzător nu va fi apelat. Pentru prevenirea scurgerilor, se recomandă utilizarea RAII (Resource Acquisition Is Initialization), de exemplu, cu pointeri inteligenți sau clase wrapper care garantează eliberarea resurselor la ieșirea din scop.

Apelarea unei excepții în destructor duce la comportament nedefinit. Dacă destructorul este apelat ca urmare a unei alte excepții (derulare a stivei), și acesta însuși aruncă o excepție neprocesată, programul se va încheia cu std::terminate. Conform standardului C++, destructorii trebuie să fie noexcept.

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

struct Resource {
    Resource() { std::cout << "Resursă achiziționată\n"; }
    ~Resource() { std::cout << "Resursă eliberată\n"; }
};

struct BadConstructor {
    Resource res; // Resursa va fi alocată înainte de aruncarea excepției
    BadConstructor() {
        std::cout << "Constructor BadConstructor\n";
        throw std::runtime_error("Eroare în constructor"); // Aruncare excepție
        // Destructorul Resource nu va fi apelat dacă excepția este aruncată înainte de finalizarea constructorului
    }
    ~BadConstructor() noexcept { // Destructorii trebuie să fie noexcept
        std::cout << "Destructor BadConstructor\n";
    }
};

struct GoodConstructor {
    std::unique_ptr<Resource> res; // Utilizarea unui pointer inteligent pentru RAII
    GoodConstructor() : res(std::make_unique<Resource>()) {
        std::cout << "Constructor GoodConstructor\n";
        // Acum, dacă se aruncă o excepție, unique_ptr garantează apelul destructorului Resource la ieșirea din scop
        // throw std::runtime_error("Eroare în constructor");
    }
    ~GoodConstructor() noexcept {
        std::cout << "Destructor GoodConstructor\n";
    }
};

struct BadDestructor {
    BadDestructor() { std::cout << "Constructor BadDestructor\n"; }
    ~BadDestructor() {
        std::cout << "Destructor BadDestructor\n";
        // Nu trebuie să arunci excepții dintr-un destructor, mai ales în timpul unwinding
        // throw std::runtime_error("Eroare în destructor");
    }
};

int main() {
    // Exemplu cu constructor (duce la scurgere de resurse fără RAII)
    try {
        // BadConstructor bc; // Dezactivat pentru demonstrație
    } catch (const std::exception& e) {
        std::cout << "Excepție prinsă: " << e.what() << "\n";
    }

    // Exemplu cu constructor bun (folosește RAII)
    try {
        // GoodConstructor gc; // Dezactivat pentru demonstrație, nu va exista scurgere
    } catch (const std::exception& e) {
        std::cout << "Excepție prinsă: " << e.what() << "\n";
    }

    // Exemplu cu destructor (poate duce la std::terminate)
    try {
        // BadDestructor bd; // Dezactivat, dacă vrei să vezi un terminate potențial
        // throw std::runtime_error("Altă excepție"); // Excepție care va declanșa unwinding
    } catch (const std::exception& e) {
        std::cout << "Excepție prinsă: " << e.what() << "\n";
    }

    return 0;
}

Tabel rezumat:

Locul apelului excepției Probleme potențiale Soluție recomandată
Constructor Scurgere de resurse Folosiți RAII (pointeri inteligenți, wrapper-uri)
Destructor Comportament nedefinit, std::terminate în timpul unwinding Destructorii trebuie să fie noexcept, evitarea aruncării excepțiilor