Как часто вы проверяете наличие исключения в новой инструкции C ++? - PullRequest
8 голосов
/ 30 декабря 2008

Я только начал читать Effective C ++ сегодня и дошел до того, что автор говорит об операторе new.

Книга очень хорошо объясняет, как вы можете поймать (с разной степенью элегантности) исключение std :: bad_alloc, которое может вызвать оператор new, если у вас заканчивается память.

У меня вопрос: как часто вы проверяете случай, когда не хватает памяти для создания объекта, если он вообще существует? и почему? Стоит ли хлопот?

Ответы [ 10 ]

18 голосов
/ 30 декабря 2008

Я улавливаю исключения, когда могу ответить на этот вопрос:

Что вы будете делать с исключением, как только поймали его?

В большинстве случаев мой ответ таков: «Понятия не имею. Может, мой звонящий знает». Так что я не ловлю исключение. Пусть это всплывет кому-то, кто знает лучше.

Когда вы улавливаете исключение и позволяете своей функции продолжить работу, вы говорите своей программе: «Не бери в голову. Здесь все хорошо». Когда вы говорите, что, ей-богу, все должно быть хорошо. Так что, если у вас не хватает памяти, то после обработки std::bad_alloc у вас больше не должно быть нехватки памяти. Вы не должны просто возвращать код ошибки из вашей функции, потому что тогда вызывающая сторона должна явно проверить этот код ошибки, и вам все еще не хватает памяти. Ваша обработка этого исключения должна освободить немного памяти. Очистите некоторые кэши, зафиксируйте некоторые данные на диск и т. Д. Но сколько функций в вашей программе вы действительно хотите нести за сокращение использования памяти вашей программой?

Если вы не можете решить проблему, вызвавшую исключение, не обрабатывайте исключение.

7 голосов
/ 30 декабря 2008

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

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

4 голосов
/ 30 декабря 2008

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

4 голосов
/ 30 декабря 2008

Я думаю, что самое важное - всегда осознавать, что у вас может не хватить памяти. Затем решите, волнует ли вас это или нет. Подумайте о том, чтобы попробовать и поймать для каждого распределения - это много хлопот. Выберите между повышенной производительностью и более простым кодом по сравнению с приложением, которое может изящно справиться со случаем отсутствия памяти. Я думаю, что оба выигрыша чрезвычайно важны в правильном контексте, поэтому выбирайте осторожно.

Да, вы можете упростить свою жизнь, определив базовый класс шаблона, который предоставляет пользовательский оператор new и оператор delete, а также устанавливает новый обработчик new. Затем вы можете использовать шаблон Curily Recurring Template для получения из этого базового класса. Затем ваши производные классы будут корректно обрабатывать неправильные распределения, но вам все равно нужно помнить, что наследовать от этого базового класса нужно для каждого нового создаваемого вами класса. Часто вы можете получить множественное наследование, что может привести к сложностям. Независимо от того, что вы делаете для обработки неправильных распределений, ваш код не будет таким простым, как если бы вы не беспокоились.

На этот вопрос никогда не бывает одного ответа. Это выбор, который вы должны сделать, в зависимости от контекста, как всегда.

2 голосов
/ 30 декабря 2008

Я думаю, это во многом зависит от типа приложений, которые вы пишете. Если бы я написал что-то, что не влияет на глобальную стабильность системы, скажем, игру или проигрыватель фильмов, я бы не стал проверять это исключение. Мое приложение будет вызывать std::terminate, и я могу его где-нибудь зарегистрировать, или мое ядро ​​сначала убьет мою программу, чтобы освободить место для других программ.

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

Я думаю, что все зависит от пропорциональности. Что вы получаете от удивительно стабильного проигрывателя фильмов, если он начинает замедляться при воспроизведении фильмов из-за вашей агрессивной проверки?

Кстати, кто-то ответил, что malloc не вернет 0, если у вас недостаточно памяти для некоторых систем. Это верно, но, как указывает руководство по malloc (специфично для Linux)

В случае, если Linux используется в обстоятельствах, когда было бы менее желательно внезапно потерять некоторые случайно выбранные процессы, и, кроме того, версия ядра является достаточно новой, можно отключить это чрезмерное выполнение, используя команду, подобную: $ echo 2 > /proc/sys/vm/overcommit_memory

См. Также каталог документации ядра, файлы vm / overcommit-accounting и sysctl / vm.txt.

2 голосов
/ 30 декабря 2008

Отказ от обработки исключения приведет к сбою вашей программы, а полученные вами запросы поддержки будут где-то между «не работает», «случайно падает» и «Я потерял всю свою работу в тот день». Если вы думаете, что это нормально, тогда это действительно не стоит хлопот.

Самое меньшее, что вы можете сделать, это сказать пользователю, что ему действительно не хватило памяти, по крайней мере, дать ему (или поддержать) подсказку, почему приложение случайно падает.

Кроме того, вы можете попытаться сохранить результаты, например, сохраняя их в файл восстановления. Это может быть легче сделать, прежде чем столкнуться с проблемой.

Было бы невероятно, если бы вы могли зайти так далеко, чтобы выдать сообщение об ошибке типа «Вы не можете вставить это изображение, потому что у вас закончилась память». И ты продолжишь работать, как будто ничего не случилось. Однако это будет означать, что весь код пользовательской команды должен быть транзакционным и давать строгое исключение, гарантирующее безопасность .


Итак, определите стоимость случайного исчерпания памяти. Исходя из этого, оцените, какой «уровень защиты» вы должны предоставить своему пользователю.

2 голосов
/ 30 декабря 2008

В системе, использующей виртуальную память, malloc () не возвращает NULL, а new не возвращает std :: bad_alloc; они вернут виртуальный адрес. При записи в зону памяти, указанную этим адресом, система попытается сопоставить виртуальный адрес с физическим адресом. Если памяти больше нет, вы получите ошибку страницы.

Итак, вы ловите std :: bad_alloc, когда находитесь во встроенной системе без MMU, и надеетесь, что сможете сделать что-нибудь для освобождения памяти.

0 голосов
/ 02 января 2009

Раньше ваша программа умерла от смерти подкачки прежде, чем вы выделили свой последний возможный байт, когда жесткий диск получил доступ к файлу подкачки размером вашего адресного пространства. Но в современных системах с 4 ГБ + и 32-разрядными процессами такое поведение встречается гораздо реже. Даже самые большие процессы могут получить всю физическую память, которую они могут обработать. В этих случаях они могут исчерпать память до того, как жесткий диск умрет.

Хотя нет общей стратегии обработки. Любой процесс, в котором реализованы кэши, должен их очищать, но хороший кэш уже был бы очищен, когда ОС сигнализировала о нехватке памяти. Процесс, который отвечает на пользовательские запросы, может обрабатывать bad_alloc на уровне детализации пользовательских запросов. Обычно мало пользы в сохранении выделенной памяти, если вам не хватает памяти для действий пользователя. Вместо этого вернитесь к состоянию до действия пользователя. С другой стороны, неинтерактивный процесс может переключиться с алгоритма O (N) на более медленный алгоритм O (N log N).

0 голосов
/ 31 декабря 2008

Обычно это не стоит, если вы не используете что-то вроде шаблона RAII (Resource Acquisition Is Initialization). В этом случае вы, вероятно, выделяете удаленные ресурсы в конструкторе, что может включать использование new для создания большого буфера.

В этом случае, вероятно, лучше поймать исключение, поскольку вы находитесь в конструкторе. Кроме того, поскольку это в RAII, вероятно, просто ресурсу требуется слишком много памяти, что позволяет предоставлять пользователю более описательные сообщения об ошибках.

0 голосов
/ 30 декабря 2008

Если вам нужно выделить память, например, для буфер пути, в котором, как вы знаете, будет только несколько байтов, может не стоить хлопот.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...