Является ли память автоматически исправлена ​​при вызове конструктора делегирования? - программирование
Подтвердить что ты не робот

Является ли память автоматически исправлена ​​при вызове конструктора делегирования?

Вслед за этим: Вызывается деструктор при вызове конструктора делегирования?

class X
{
public:
    X()       {};
    X(int)    : X() { throw std::exception(); }
    X(double)       { throw std::exception(); }
    ~X();
};

Как насчет динамической памяти? Обычно исключение в конструкторе означает, что объект не был полностью сконструирован и, следовательно, память очищена и не потеряна.

Но аргумент в предыдущем вопросе состоит в том, что объект полностью сконструирован (или полностью инициализирован) после завершения делегирования. Как это влияет на восстановление памяти? Я надеюсь, что память все еще очищена!

int main()
{
    new X(5);        // new called 
                     // delete called because delegate completed.
                     // I assume:  
                     //      Memory re-claimed (because constructor did not complete)
                     //      I assume the C++11 standard adjusted to compensate.
                     //      As a constructor did complete.
}

Сравнено также:

int main()
{
    new X(5.0);      // new called 
                     //    Delete **NOT** called
                     // Memory re-claimed (because constructor did not complete)
}

Если память очищена, тогда определение того, когда происходит очистка памяти, необходимо изменить с помощью спецификации С++ 03. Как изменилось поведение?

4b9b3361

Ответ 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