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 деструктору чакырылбайт, анткени 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-дан мурда) чакырылбай калышы мүмкүн.