Почему Деструктор в C ++ выделяет память в обратном порядке, как они были инициализированы? - PullRequest
14 голосов
/ 30 августа 2011

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

Ответы [ 3 ]

19 голосов
/ 30 августа 2011

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

Type1 Object1;
Type2 Object2(Object1);

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

12 голосов
/ 30 августа 2011

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

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

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

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

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

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

и т. Д. И т. Д. *

8 голосов
/ 30 августа 2011

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

{
    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 ++, например, подобъекты базового класса уничтожаются после запуска деструктора производного класса, и что члены данных объекта уничтожаются в обратном порядке построения, также после того, как деструктор производного классазапустить и перед подобъектами базового класса.Здесь есть каждая гарантия, так что вы можете написать код, который каким-то образом полагается на вторую вещь, которая все еще существует, пока первая уничтожена.

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

...