Ответ 1
Если конструктор, вызываемый new
, генерирует исключение, то память, выделенная new
, автоматически освобождается. Делегирующие конструкторы ничего не меняют в этом отношении.
Если какая-либо часть инициализации объекта, описанная выше 76 завершается путем выброса исключения и может быть найдена подходящая функция освобождения, функция освобождения вызывается для освобождения памяти, в которой объект был сконструирован
— С++ 11 [expr.new] 5.3.4/18
Описанная "любая часть инициализации объекта" включает как вызовы конструктора, так и оценку выражений, переданных конструктору.
Кроме того, это поведение задается тождественно в стандарте С++ 98 [С++ 98 5.4.3/17]. Единственные отличительные конструкторы делегирования заключаются в том, что ваша ментальная модель ранее была основана на полностью построенном объекте или нет. Учитывая делегирование конструкторов, которые больше не эквивалентны фактической спецификации, когда происходит освобождение.
В первом примере:
new X(5);
Порядок событий:
- функция распределения называется
- X (int) называется
-
- X() вызван (и успешно завершен)
-
- X (int) генерирует исключение
-
- ~ X() называется
- X (int) завершает исключение
- функция освобождения вызвана, потому что не удалось инициализировать объект
- исключение продолжает распространяться нормально
Во втором примере
new X(5.0);
- функция распределения называется
- X (двойной), называемый
- Ошибка X (double) с исключением
- функция освобождения вызвана, потому что не удалось инициализировать объект
- исключение продолжает распространяться нормально
Это поведение можно наблюдать, заменяя функции распределения и освобождения:
#include <iostream>
#include <cstdlib>
#include <stdexcept>
#include <new>
void *operator new(std::size_t s) {
if (void *ptr = std::malloc(s)) {
std::cout << "allocation\n";
return ptr;
}
throw std::bad_alloc{};
}
void operator delete(void *ptr) noexcept {
if (ptr) {
std::cout << "deallocation\n";
std::free(ptr);
}
}
struct S {
S() {};
S(int) : S{} { throw std::exception(); }
S(double) { throw std::exception(); }
~S() { std::cout << "destructor\n"; }
};
int main() {
std::cout << "test 1\n";
try {
new S(1);
} catch(...) {
std::cout << "exception caught\n";
}
std::cout << "test 2\n";
try {
new S(1.);
} catch(...) {
std::cout << "exception caught\n";
}
}
Правильный вывод этой программы:
test 1
allocation
destructor
deallocation
exception caught
test 2
allocation
deallocation
exception caught