Երբ է բացառում, որ երբ արտահայտում եք արտահայտություն (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-ից առաջ), կարող են չլինել կանչված։