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

15
karambir 30 Авг 2011 в 13:48

3 ответа

Лучший ответ

Рассмотрим этот пример:

Type1 Object1;
Type2 Object2(Object1);

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

21
Armen Tsirunyan 30 Авг 2011 в 09:50

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

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

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

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

Когда вы помните об этом, многие ошибки C ++ объясняются

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

И т.п ...

12
spraff 30 Авг 2011 в 10:08

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

{
    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 для этого не приводит к созданию пуленепробиваемого кода в этом отношении. Но даже с этой проблемой мы, по крайней мере, гарантируем, что к моменту снятия блокировки у нас больше не будет открытого файла, и в целом это своего рода полезная гарантия, которую может предоставить порядок уничтожения.

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

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

8
Steve Jessop 30 Авг 2011 в 10:12