Sobes.tech
Middle+

Երբ է բացառում, որ երբ արտահայտում եք արտահայտություն (exception), ոչ մի դեստրուկտոր չի կանչվում?

sobes.tech AI

Պատասխան AI-ից

Տեղական օբյեկտների դեստրուկտորները, որոնք ունեն ավտոմատ կյանքի ժամանակ, չեն կանչվում, եթե կոնստրուկտորում արտահայտություն է նետվում և օբյեկտը ամբողջությամբ չի ստեղծվել:

Նույնպես, դեստրուկտորները չեն կանչվում հետևյալ դեպքերում՝

  • Գլոբալ կամ ստատիկ կյանքի ժամանակ ունեցող օբյեկտների համար։
  • Սուր հիշողության օբյեկտների համար, որոնք կառավարում են ստանդարտ մեխանիզմներ (օրինակ, malloc-ով հատկացված և չազատագրված)։
  • Օբյեկտների համար, որոնց ցույց են տալիս թափանցող խելացի ցուցիչները։
  • Օբյեկտների համար, որոնց դեստրուկտորները նշված են որպես noexcept(false) և ինքնուրույն արտածում են արտահայտություն՝ ժամանակի ընթացքում (stack unwinding)։ Այս դեպքում ծրագրը սովորաբար կավարտվի սխալի հետ (std::terminate

Օրինակ՝ դեստրուկտորի չկանչը կոնստրուկտորում՝

#include <iostream>
#include <stdexcept>

class Resource {
public:
    Resource() {
        std::cout << "Resource acquired" << std::endl;
    }
    ~Resource() {
        std::cout << "Resource released" << std::endl;
    }
};

class MyClass {
public:
    Resource res1; // Այս դեստրուկտորը կկանչվի, եթե արտահայտություն է նետվում MyClass-ի կոնստրուկտորում
    // Resource res2; // Եթե այստեղ նետվի արտահայտություն, res2 չի ստեղծվի

    MyClass() {
        // res1 արդեն ստեղծված է
        std::cout << "MyClass constructor" << std::endl;
        throw std::runtime_error("Error in MyClass constructor");
        // Resource res2 = Resource(); // Այս օբյեկտը չի ստեղծվի, եթե վերևում նետվի արտահայտություն
    }
    ~MyClass() {
        std::cout << "MyClass destructor" << std::endl;
    }
};

int main() {
    try {
        MyClass obj; // Այստեղ արտահայտություն է նետվում
    } catch (const std::runtime_error& e) {
        std::cerr << "Caught exception: " << e.what() << std::endl;
    }
    return 0;
}

Այս օրինակով, MyClass դեստրուկտորը չի կանչվում, քանի որ կոնստրուկտորը չի ավարտվել հաջողությամբ։ Սակայն, res1 (որը պատկանում է MyClass) դեստրուկտորը կկանչվի, քանի որ res1-ը հաջողությամբ ստեղծվել է, նախքան արտահայտության նետումը։

Օրինակ՝ կրկնվող արտահայտության դեպքում՝

#include <iostream>
#include <stdexcept>

class BadDestructor {
public:
    BadDestructor() {
        std::cout << "BadDestructor constructor" << std::endl;
    }
    ~BadDestructor() noexcept(false) {
        std::cout << "BadDestructor destructor" << std::endl;
        throw std::runtime_error("Error in destructor"); // Նետում է արտահայտություն դեստրուկտորում
    }
};

void foo() {
    BadDestructor obj;
    throw std::runtime_error("Exception in foo");
}

int main() {
    try {
        foo();
    } catch (const std::runtime_error& e) {
        std::cerr << "Caught exception: " << e.what() << std::endl;
    }
    // `std::terminate` կկանչվի, նախքան այս բլոկը
    return 0;
}

Այս դեպքում, երբ foo-ում արտահայտություն է նետվում, սկսվում է ստեկի վերածնունդը։ Կոչվում է BadDestructor-ի դեստրուկտորը։ Քանի որ BadDestructor-ի դեստրուկտորը նետում է արտահայտություն (noexcept(false) թույլ է տալիս դա), տեղի է ունենում կրկնակի արտահայտություն՝ ընթացիկ արտահայտության վերամշակման ժամանակ։ Ստանդարտի համաձայն, այսպիսի իրավիճակում կանչվում է std::terminate, և ծրագիրը ավարտվում է։ Այլ օբյեկտների դեստրուկտորները, որոնք գտնվում էին ստեկում (եթե եղել են foo-ից առաջ), կարող են չլինել կանչված։