На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Правила раздела Visual C++ / MFC / WTL (далее Раздела)
1) На Раздел распространяются все Правила Форума.
2) Перед тем, как создать новый топик, убедитесь, что Вы читали Правила создания тем в Разделе.
3) Вопросы, не связанные с программированием (настройки MS Visual Studio, книги, библиотеки и т.д.),
обсуждаются в разделе C/C++: Прочее
4) Вопросы разработки .NET (Windows Form, C++/CLI и т.п.) приложений на Visual C++/C# обсуждаются в разделе .NET.
5) Нарушение Правил может повлечь наказание со стороны модераторов.

Полезные ссылки:
user posted image FAQ Раздела user posted image Обновления для FAQ Раздела user posted image Поиск по Разделу user posted image MSDN Library Online
Модераторы: ElcnU
  
> Использование исключений
    Всем привет.
    Исключения и производительность
    Тема 2005 года, но что на гуглилось...
    Немного интернет почитал, но так ясности и не появилось.
    Но кроме быстродействия, меня интересует целесообразность try/catch и(или) if/else и в каких местах лучше делать.

    Visual Studio 2019
    Настройка исключения по умолчанию: /EHsc

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

    1) Переживание за работу функций за пределами моей программы.
    Это работа с сетевыми функциями средствами ОС: NetServerGetInfo и SNMP, и библиотекой MySQL.
    Как я понял документацию, внешние функции (extern "C") подразумеваются компилятором, что они не вызывают исключения.
    ExpandedWrap disabled
      NET_API_STATUS nasRes = -1;
      try
      {
          nasRes = NetServerGetInfo((LPWSTR) addr.c_str(), 101, (LPBYTE *) &pSI101);
      } // try
      catch (...) {} // catch
      if (nasRes == NERR_Success)

    Т.е. в данном блоке кода надо убрать try/catch? Даже если функция и вызывает исключения, то согласно /EHc компилятор предполагает, что нет исключения и он не с генерирует нужный код?
    Мне не известно, кроме возврата результата, может ли вызывать эта функция (и другие внешние) исключение или нет, чтоб их можно было перехватывать? Или это как сделает разработчик?
    Надо что-то переделывать (например, поменять настройку исключение), чтоб предотвратить крах программы?

    Еще один момент меня смущает: например, работа с базой MySQL (сторонняя dll), предполагаю, что в работе этих внешних функция идет выделение памяти, что произойдет при нехватке памяти в ОС?
    Если сторонний разработчики сделали кучу проверок, и вернул ошибку и всегда ли возможно это будет сделать: корректно вернуть ошибку без краха программы? А если нет, какая дальнейшая ситуация?
    Временный сбой таких функций не является фатальным, кроме случая, когда серьезные проблемы в ОС (например, из-за нехватки памяти).
    Например, стал недоступен сервер базы в момент какой-либо записи на него, например, проблема с сетью, после восстановления сети можно будет дальше работать.
    Сделано так: в начале/конце функции записи в базу стоит try/catch с установкой флага, корректно записалась информация или нет, запись (вызов функции) производиться по таймеру.

    2) Второй аспект, это у меня в программе тоже много работы с памятью: выделение/удаление, но среднее потребление в одном районе:
    Считывается информация с устройства и под нее выделяется необходимая память. Потом надо повторно считать новую информацию. Но считывается уже в новую область памяти, и если эта информация корректна (делаются проверки), то старая память освобождается и сохраняется ссылка на новый участок памяти.
    Ну и в процессе работы так же память мелкими партиями может выделяться/удаляться (конвертирование строк и т.д.).
    Так вот, если уже есть нехватка памяти, то программа уже не сможет работать, ей нужна память для считывания информации, т.е. это фатальная ситуация.
    Стоит ли заморачиваться проверкой всех выделений памяти или где-нибудь на верхнем уровне поставить try/catch с запуском процедур по завершению работы программы?
      Цитата Black_Dragon @
      Т.е. в данном блоке кода надо убрать try/catch? Даже если функция и вызывает исключения, то согласно /EHc компилятор предполагает, что нет исключения и он не с генерирует нужный код?
      Мне не известно, кроме возврата результата, может ли вызывать эта функция (и другие внешние) исключение или нет, чтоб их можно было перехватывать? Или это как сделает разработчик?
      Надо что-то переделывать (например, поменять настройку исключение), чтоб предотвратить крах программы?

      Да, надо убрать try/catch и проверять код возврата. Юзаешь функцию - читаешь доку, если функция может кидать исключение - оборачиваешь в try/catch, если не может - можешь не оборачивать. Заниматься тотальной оберткой всего и вся так же не стоит. Возможно стоит подойти архитектурно к этому вопросу. Например у тебя функция кидает исключение, необязательно оборачивать именно ее в try/catch - возможно достаточно будет обернуть в try/catch вызывающий код.
      Оборачивать в try/catch - Обычно нужно там, где тебе нужно именно обработать ошибку(записать сообщение в лог например, или попытаться что то пользователю показать чтоб он исправил или еще чего то сделать в конкретно этом месте), если в этом конкретном месте не нужно обрабатывать ошибку, можно не оборачивать, оно полетит вверх по стеку и словится в другом месте, выше по коду, главное чтоб выше по стеку вызовов код был обернут в try/catch

      Цитата Black_Dragon @
      2) Второй аспект, это у меня в программе тоже много работы с памятью: выделение/удаление, но среднее потребление в одном районе:
      Считывается информация с устройства и под нее выделяется необходимая память. Потом надо повторно считать новую информацию. Но считывается уже в новую область памяти, и если эта информация корректна (делаются проверки), то старая память освобождается и сохраняется ссылка на новый участок памяти.
      Ну и в процессе работы так же память мелкими партиями может выделяться/удаляться (конвертирование строк и т.д.).
      Так вот, если уже есть нехватка памяти, то программа уже не сможет работать, ей нужна память для считывания информации, т.е. это фатальная ситуация.
      Стоит ли заморачиваться проверкой всех выделений памяти или где-нибудь на верхнем уровне поставить try/catch с запуском процедур по завершению работы программы?

      Если программа отвалилась/завершилась ОС за тебя освободит всю ранее выделенную память под этому программу, включая ту память, которую сама программа выделила и не освободила.
      Нехватка памяти при выделении(если ты не работаешь с большими объемами данных) - редкое явление, поэтому нужно смотреть по ситуации. Если ты выделяешь память на 10000000 элементов типа long, то возможно нужно проверять, а если на 1 переменную типа Int, то не стоит. Но указатель на валидность, перед использованием проверять нужно всегда и обязательно. А еще лучше юзать готовые структуры данных для работы с памятью std::string/std::vector/std::stack/std::deque и т.д., в зависимости от постановки задачи. Данные структуры упрощают работу с памятью и с содержимым и предотвращают появление стандартных детских ошибок.

      Добавлено
      Цитата Black_Dragon @
      Еще один момент меня смущает: например, работа с базой MySQL (сторонняя dll), предполагаю, что в работе этих внешних функция идет выделение памяти, что произойдет при нехватке памяти в ОС?
      Если сторонний разработчики сделали кучу проверок, и вернул ошибку и всегда ли возможно это будет сделать: корректно вернуть ошибку без краха программы? А если нет, какая дальнейшая ситуация?
      Временный сбой таких функций не является фатальным, кроме случая, когда серьезные проблемы в ОС (например, из-за нехватки памяти).
      Например, стал недоступен сервер базы в момент какой-либо записи на него, например, проблема с сетью, после восстановления сети можно будет дальше работать.
      Сделано так: в начале/конце функции записи в базу стоит try/catch с установкой флага, корректно записалась информация или нет, запись (вызов функции) производиться по таймеру.

      Нужно читать доку/спеку по конкретно этим функциям. Они могут как кидать исключения так и возвращать код ошибки, возможно и то и другое, в зависимости от какого нибудь флага в конфиге. Соответственно и плясать нужно исходя из этого. Если в доке написано что функция кидает исключение - оборачивай в try/catch, если написано что возвращает ошибки в коде возврата - значит проверяй код возврата.
      Сообщение отредактировано: Wound -
        /EHsc
        Говорит компилятору, что внешние функции не могут кидать исключение.
        Значит, если у меня будут использоваться внешние функции, которые могут кидать исключение, то надо поменять настройку проекта на /EHa или /EHr?

        У меня структуры, где членами является много полей string/list/vector/map и ссылки на похожие дополнительные другие структуры.

        Там, где использую new, у меня стоит проверка с if-ом, но тут надо корректно допилить код, чтоб наверху понимали, что типа это сбой системы, а не то, что нет информации в устройстве (а это штатное поведение).
        Т.е. сделано так, возвращается: если не null, то это указатель на структуру с информацией, если null, то нет данных или не удалось выделить память. Программа может выполнять часть своих функций и при не хватке памяти.
        А если память появится, то продолжит полноценно работать. У нас используется виртуализация с динамической памятью, ну и на сервере тоже может крутиться и другое ПО. Все не смоделируешь.

        А вот может быть еще такое: надо битовый массив визуализировать в текст, т.е. для std::string будет выделена память. А памяти нет.
        Оборачивать в try/catch? Я это делаю, но на верхнем уровне, но где-то подавляю, а где-то начинаю завершать работу программы.

        Встает вопрос, на каком уровне вложенности делать try/catch, на всех или на самом верхнем.

        Код работает корректно, утечек памяти нет.
        Цель стоит: защита работы программы при работе с внешними функциями в случае, когда этот сбой не фатальный и он со временем сам устраниться.
        И работа с памятью, а эти проблемы фатальны.
        Но мне кажется, что везде пихать проверки - это накладно с точки написание кода и процесса информирования назад на верх. Пока разрабатывал, везде делал проверки, защиты и т.д., так как есть у меня и вложенные try/catch в вызываемой и вызывающей функциях. Т.е. адекватно почистить, привести в порядок код, не перегружая его.

        Но хочется соблюсти феншуй (есть ли он и какой не знаю). Конечный код, который вызывается кодом ядра программы или еще промежуточным кодом, является как бы законченным/осмысленным кодом библиотек, который так выглядит и его можно, если надо, где-нибудь еще применить (скопировав файлы).
        Так вот, как правильнее, фатальные ошибки обрабатывать сразу же на месте через исключение и информировать каким-нибудь кодом возврата или пусть на верхнем уровне ловят исключения?
        Из нагугленного я пока не понял, есть ли накладные расходы на использование try/catch для Visual Studio (std:c++14).
        Хотя компилятор для некоторых функций выводит предупреждения, что ее надо пометить как noexcept. Значит все остальное потенциально опасно.
          Цитата Black_Dragon @
          /EHsc
          Говорит компилятору, что внешние функции не могут кидать исключение.
          Значит, если у меня будут использоваться внешние функции, которые могут кидать исключение, то надо поменять настройку проекта на /EHa или /EHr?

          Под внешними функциями ты имеешь ввиду extern "C" ? Если да, то сишные функции не могут кидать исключения С++, в языке нет для этого стандартных механизмов. Я думаю на данном этапе тебе следует игнорировать эти настройки, и пользоваться настройкой по умолчанию.

          Цитата Black_Dragon @
          Встает вопрос, на каком уровне вложенности делать try/catch, на всех или на самом верхнем.

          Так это все зависит от того - где тебе нужно. Можно вообще не писать try/catch, тогда будет вылетать стандартный обработчик исключений, и ты будешь видеть окно с исключением, после чего программа завершится. Чтоб этого окна не было - ты вставляешь свои обработчики, и соответственно там обрабатываешь поведение.
          Допустим у тебя выполняется какая то функция, которая взаимодействует с каким то девайсом(например флешкой), пользователь допустим хочет что то записать на эту флешку - и в процессе, где то внутри функция, которая непорседственно обратилась к флешке и начала запись - в процессе записи сгенерировалось исключение "out of memory", соответственно ты можешь обернуть в try/catch непосредственно саму функцию в которой сгенерировалось исключение, записать код ошибки и текст в лог файл и послать исключение выше по стеку, пока его не перехватит обработчик выше и не выдаст пользователю что произошла ошибка. А для себя ты получил - конкретное место, на каком именно этапе произошла ошибка и при каких критериях. Либо ты можешь его не перехватывать непосредственно там - где идет запись на флешку, а перехватить выше, все зависит от того - как тебе нужно.


          Цитата Black_Dragon @
          Так вот, как правильнее, фатальные ошибки обрабатывать сразу же на месте через исключение и информировать каким-нибудь кодом возврата или пусть на верхнем уровне ловят исключения?
          Из нагугленного я пока не понял, есть ли накладные расходы на использование try/catch для Visual Studio (std:c++14).
          Хотя компилятор для некоторых функций выводит предупреждения, что ее надо пометить как noexcept. Значит все остальное потенциально опасно.

          Если ты можешь в конкретном месте исправить ошибку или как то повлиять на ход программы - то ловить в этом месте, если не можешь то пропускать дальше.
          Допустим у тебя есть бесконечный цикл, в котором что то выполняется, допустим игра, этот бесконечный цикл внутри функции main, допустим пользователь нажал кнопку "Играть" и где-то внутри, у тебя там полетело исключение - "Нет сети", ты можешь его словить внутри цикла - и показать пользователю, и дальше продолжить выполнение, пользователь увидит сообщение, поймет что нет интернета или с интернетом что то не то, дальше пойдет починит свой интернет и еще раз нажмет кнопку "Играть". Ну а можно например обернуть сам цикл в try/catch - соответственно возбуждение исключения выкинет тебя из цикла в главную функцию main, дальше ты покажешь пользователю окно с ошибкой: "Нет сети" и программа завершится.
          Оба поведения корректны, но какой именно способ подходит тебе - решать только тебе.
          Сообщение отредактировано: Wound -
            Цитата Wound @
            Если ты можешь в конкретном месте исправить ошибку или как то повлиять на ход программы - то ловить в этом месте, если не можешь то пропускать дальше.

            Во-во, такое ощущение и есть.

            Но есть одна ситуация, связанная с вызовом системных функций, которые выделяют системную память, и эту память надо освобождать вызовом другой системной функции.
            Пример, NetServerGetInfo/NetApiBufferFree.
            С учетом того, что, например, в том же string может произойти исключение, и сразу же выкинит на уровень выше, то память эта не освободиться.
            Есть такие варианты действия:
            1) Исходя из того, что это исключение связано с нехваткой памяти и дальнейшие действия будут по закрытию программы, то нет смысла заморачиваться освобождением этой памяти и проигнорировать.
            2) Делать внутри try\catch, освобождать память и кидать на верх исключение.
            3) Делать класс-обвертку с деструктором, чтоб при выбросе на верх запускалось удаление.

            По пункту 1) с учетом того, что при любом закрытии программы, будет освобождена ее память, то не зачем беспокоиться об порядке. Но, если это аварийно закрытие (ОС прибила), то программа закроется по любому, а если просто нехватка памяти (стадия, когда самой ОС еще хватает для жизни), то работа программы не прервется и если ядро решит продолжить работу (разработчик решил поменять логику или этот код уже используется в другом проекте), то получим утечку памяти.
            По 2 и 3 - при любых обстоятельствах наводи за собой порядок.
            Получается, выбор между 2 и 3.
              Цитата Black_Dragon @
              Но есть одна ситуация, связанная с вызовом системных функций, которые выделяют системную память, и эту память надо освобождать вызовом другой системной функции.
              Пример, NetServerGetInfo/NetApiBufferFree.
              С учетом того, что, например, в том же string может произойти исключение, и сразу же выкинит на уровень выше, то память эта не освободиться.
              Есть такие варианты действия:
              1) Исходя из того, что это исключение связано с нехваткой памяти и дальнейшие действия будут по закрытию программы, то нет смысла заморачиваться освобождением этой памяти и проигнорировать.
              2) Делать внутри try\catch, освобождать память и кидать на верх исключение.
              3) Делать класс-обвертку с деструктором, чтоб при выбросе на верх запускалось удаление.

              Для этого в С++ есть идиома RAII, советую вам с ней ознакомится, можете прямо в google вбить идиома RAII, и почитать про нее. В С++ есть конструкторы и деструкторы, есть правила создания объекта, и есть исключения которые очень хорошо выполняют все эти правила. Если вы возьмете за правило в конструкторе выделять память, а в деструкторе освобождать - то вам не нужно будет беспокоится о том, как освобождать память, когда вылетело исключение. Потому как когда вылетает исключение, происходит глобальная раскрутка стека, с обязательным вызовом всех деструкторов у уже созданных объектах.
              Для ознакомления:
              Получение ресурса есть инициализация
              Идиома RAII — захват ресурса есть инициализация

              Добавлено
              Цитата Black_Dragon @
              NetServerGetInfo/NetApiBufferFree.

              В простейшем случае пишется класс-обертка. В конструкторе вызов NetServerGetInfo, в деструкторе вызов NetApiBufferFree. Ну и еще делается член класса, хранящий указатель от результата NetServerGetInfo.

              Далее когда в конструкторе память выделена, дальше вы пользуетесь этой переменной, если произошло исключение - гарантированно будет вызов деструктора, даже если вы это исключение не обрабатывали, соответственно память гарантировано будет очищена.
                Пункт 3 про это и был, чтоб все возложить на конструкторы/деструкторы.
                Вообщем общее направление ясно, будем наводить порядок.
                  Цитата Black_Dragon @
                  Пункт 3 про это и был, чтоб все возложить на конструкторы/деструкторы.

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

                    А вот возможно ли в UnitTest как-то протестировать работу функций на реагирование исключений...
                      Цитата Black_Dragon @
                      А вот возможно ли в UnitTest как-то протестировать работу функций на реагирование исключений...

                      Я думаю да, нужно в документации поискать, я студийными UT не пользовался.
                      0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                      0 пользователей:


                      Рейтинг@Mail.ru
                      [ Script execution time: 0,0424 ]   [ 17 queries used ]   [ Generated: 26.04.24, 21:34 GMT ]