Подтвердить что ты не робот

Почему Destructor в С++ деблокировал память в обратном порядке, как они были инициализированы?

В чем преимущество при распределении памяти в обратном порядке к переменным?

4b9b3361

Ответ 1

Рассмотрим следующий пример:

Type1 Object1;
Type2 Object2(Object1);

Предположим, что Object2 использует некоторые внутренние ресурсы Object1 и действует до тех пор, пока Object1 действителен. Например, Object2 деструктор обращается к внутреннему ресурсу Object1. Если бы не гарантия обратного порядка уничтожения, это привело бы к проблемам.

Ответ 2

Это не просто о освобождении памяти, а о симметрии в более широком смысле.

Каждый раз, когда вы создаете объект, вы создаете новый контекст для работы. Вы "нажимаете" на эти контексты по мере необходимости и снова "поп" назад - необходима симметрия .

Это очень мощный способ мышления, когда дело доходит до RAII и безопасности исключений, или доказывая правильность w.r.t. предварительные условия и постусловия (конструкторы устанавливают инварианты, деструкторы должны assert() их, а в хорошо продуманных классах каждый метод явно сохраняет их).

ИМХО, отсутствие этой функции - это один из самых больших недостатков Java. Рассмотрим объекты, конструкторы которых открывают дескрипторы файлов или мьютексы или что-то еще - ответ Армена блестяще иллюстрирует, как эта симметрия применяет некоторые ограничения общего смысла (такие языки, как Java, могут позволить Object1 выйти из области видимости до Object2, но Object2 сохраняет Object1 живым путем подсчета ссылок), но существует целый комплекс проблем дизайна, которые аккуратно падают на место, когда рассматриваются в терминах жизни объектов.

Много С++ getchas объясняют сами, когда вы это помните

  • почему goto не может пересекать инициализацию
  • почему вам может быть рекомендовано иметь только одну return в любой функции (это относится только к языкам, отличным от RAII, таким как C и Java).
  • почему исключение является разумным способом только для отказа конструктора, а также почему деструкторы могут никогда разумно бросать
  • почему вы не должны вызывать виртуальные функции в конструкторе

и т.д. и т.д.

Ответ 3

Гарантией порядка уничтожения локальных переменных является возможность написать (например) код следующим образом:

{
    LockSession s(lock);
    std::ofstream output("filename");

    // write stuff to output
}

LockSession - это класс, который приобретает замок в своем конструкторе и освобождает его в своем деструкторе.

В } мы знаем, что дескриптор файла будет закрыт (и сброшен) до освобождения блокировки, что является очень полезной гарантией, если есть другие потоки в программе, которые используют один и тот же замок для защищать доступ к одному файлу.

Предположим, что порядок уничтожения не был определен стандартом, тогда нам придется беспокоиться о возможности того, что этот код выпустит блокировку (разрешая другим потокам доступ к файлу), и только затем установит ее промывку и закрытие, Чтобы сохранить требуемую гарантию, нам нужно написать код следующим образом:

{
    LockSession s(lock);
    {
        std::ofstream output("filename");

        // write stuff to output

    } // closes output
} // releases lock

Этот пример не является совершенным - сброс файла не гарантируется на самом деле, так что полагаться на деструктор ofstream для этого не приводит к тому, что в этом отношении не будет пуленепробиваемый код. Но даже с этой проблемой мы, по крайней мере, гарантируем, что к тому времени, когда мы выпустим блокировку, мы больше не открываем файл, и вообще, что полезная гарантия того, что порядок уничтожения может обеспечить.

Существуют также другие гарантии порядка уничтожения в С++, например, что подобъекты базового класса уничтожаются после запуска деструктора производного класса и что элементы данных объекта уничтожаются в обратном порядке построения, также после производного класса destructor запускается и перед подобъектами базового класса. Каждая гарантия существует, поэтому вы можете написать код, который каким-то образом полагается на вторую вещь, которая все еще находится там, в то время как первое уничтожается.

Ничто из этого не имеет особого отношения к фактическому де-распределению памяти, это гораздо больше о том, что делает деструктор. Тем не менее, поскольку вы спрашиваете о де-распределении, могут быть некоторые случаи, когда определенные реализации распределителя памяти выигрывают от освобождения блоков в обратном порядке их распределения. Это могло бы облегчить для распределителя сокращение фрагментации памяти путем слияния смежных свободных блоков. Вы не очень часто должны об этом думать, и в любом случае распределители, которые должны объединять свободные блоки, должны быть достаточно умны, чтобы делать это независимо от того, какой порядок они распределены и освобождены.