Я думаю, что здесь есть два вопроса.
В чем разница между throw
и throw e;
?
Я не думаю, что когда-либо есть веская причина написать catch (Exception e) { throw e; }
. Это теряет оригинальную трассировку стека. При использовании throw;
исходная трассировка стека сохраняется. Это хорошо, потому что это означает, что причину ошибки легче найти.
В чем разница между catch
и catch (Exception e)
?
Оба ваших примера одинаковы и одинаково бесполезны - они просто перехватывают исключение и затем перебрасывают Это. Одно небольшое отличие состоит в том, что первый пример будет генерировать предупреждение компилятора.
Переменная 'e' объявлена, но никогда не использовалась
Имеет смысл задать этот вопрос, если у вас есть какой-то другой код в блоке catch, который действительно делает что-то полезное. Например, вы можете захотеть записать исключение:
try
{
int value = 1 / int.Parse("0");
}
catch (Exception e)
{
LogException(e);
throw;
}
Теперь необходимо использовать первую версию, чтобы у вас была ссылка на перехваченное исключение.
Если ваш блок catch на самом деле не использует исключение, тогда вы захотите использовать вторую версию, чтобы избежать предупреждения компилятора.
Один из способов - вставить строки имени файла и номера строки (через указатель) модуля, выделяющего память, в выделенный блок данных. Номер файла и строки обрабатывается с помощью стандартных макросов C ++ « __ FILE __
» и « __ LINE __
». Когда память освобождается, эта информация удаляется.
Одна из наших систем имеет эту функцию, и мы называем ее «отчет о потере памяти». Таким образом, в любой момент из нашего интерфейса командной строки мы можем распечатать всю выделенную память вместе с большим списком информации о том, кто выделил память. Этот список отсортирован по тому модулю кода, которому выделено больше всего памяти. Мы часто будем отслеживать использование памяти таким образом с течением времени, и в конечном итоге утечка памяти всплывет вверх в списке.
Используйте умные указатели и никогда не думайте об этом снова, существует множество официальных типов, но довольно легко создать свой собственный:
class SmartCoMem
{
public:
SmartCoMem() : m_size( 0 ), m_ptr64( 0 ) {
}
~SmartCoMem() {
if( m_size )
CoTaskMemFree((LPVOID)m_ptr64);
}
void copyin( LPCTSTR in, const unsigned short size )
{
LPVOID ptr;
ptr = CoTaskMemRealloc( (LPVOID)m_ptr64, size );
if( ptr == NULL )
throw std::exception( "SmartCoMem: CoTaskMemAlloc Failed" );
else
{
m_size = size;
m_ptr64 = (__int64)ptr;
memcpy( (LPVOID)m_ptr64, in, size );
}
}
std::string copyout( ) {
std::string out( (LPCSTR)m_ptr64, m_size );
return out;
}
__int64* ptr() {
return &m_ptr64;
}
unsigned short size() {
return m_size;
}
unsigned short* sizePtr() {
return &m_size;
}
bool loaded() {
return m_size > 0;
}
private:
//don't allow copying as this is a wrapper around raw memory
SmartCoMem (const SmartCoMem &);
SmartCoMem & operator = (const SmartCoMem &);
__int64 m_ptr64;
unsigned short m_size;
};
В этом примере нет инкапсуляции из-за API, с которым я работал, но все же лучше, чем работать с полностью необработанными указателями.
Для подобного тестирования попробуйте скомпилировать встроенный код для Linux (или любой другой ОС, которую вы используете) и используйте хорошо зарекомендовавший себя инструмент, такой как Valgrind, для проверки утечек памяти. Это может быть проблемой, но вам просто нужно заменить любой код, который напрямую обращается к оборудованию, на код, имитирующий что-то подходящее для вашего тестирования.
Я обнаружил, что использование SWIG для преобразования моего встроенного кода в Linux - родная библиотека и запуск кода из скрипта Python действительно эффективен. Вы можете использовать все инструменты, доступные для невстроенных проектов, и протестировать весь свой код, кроме драйверов оборудования.
Не специально для разработки встраиваемых систем, но для этого мы использовали BoundsChecker .
Можете ли вы описать, что «не работает» с вашими методами ведения журнала?
Вы не получаете ожидаемые журналы? или они показывают, что все в порядке, но у вас все еще есть утечки?
Как вы подтвердили, что это определенно утечка, а не какой-то другой тип повреждения?
Один из способов проверить правильность вашей перегрузки: создать экземпляр счетчика объект на класс, увеличьте его в new
и уменьшите его в delete
класса. Если у вас растет счет, у вас утечка. Теперь вы ожидаете, что ваши строки журнала будут совпадать с точками увеличения и уменьшения.
Для этого можно использовать сторонний инструмент.
Вы можете обнаруживать утечки в собственных структурах классов, добавляя счетчики памяти в вызовы New и Delete для увеличения / уменьшения счетчиков памяти , и распечатайте отчет при закрытии приложения. Однако при этом не будут обнаружены утечки памяти для памяти, выделенной вне системы классов - хотя это может сделать сторонний инструмент.
Если вы используете Linux, я предлагаю попробовать Valgrind .
Есть несколько форм оператора new:
void *operator new (size_t);
void *operator new [] (size_t);
void *operator new (size_t, void *);
void *operator new [] (size_t, void *);
void *operator new (size_t, /* parameters of your choosing! */);
void *operator new [] (size_t, /* parameters of your choosing! */);
Все вышеперечисленное может существовать как в глобальной области, так и в области класса. Для каждого оператора new существует эквивалентный оператор delete. Вам необходимо убедиться, что вы добавляете ведение журнала ко всем версиям оператора, если вы хотите это сделать именно так.
В идеале вы бы хотели, чтобы система вела себя одинаково независимо от того, присутствует ли запись в памяти или не. Например, библиотека времени выполнения MS VC выделяет больше памяти при отладке, чем в выпуске, потому что она ставит перед выделением памяти больший информационный блок и добавляет защитные блоки в начало и конец выделения. Лучшее решение - хранить всю информацию журнала памяти в виде отдельного фрагмента или памяти и использовать карту для отслеживания памяти. Это также можно использовать для проверки правильности памяти, переданной для удаления.
new
allocate memory
add entry to logging table
delete
check address exists in logging table
free memory
Однако вы пишете встроенное программное обеспечение, в котором обычно память является ограниченным ресурсом. Обычно в этих системах предпочтительнее избегать динамического распределения памяти по нескольким причинам:
Итак, при выполнении встроенной работы вы обычно заранее знаете, сколько памяти можно выделить для различных объектов, и, зная это,
То, как мы сделали это с помощью нашего инструментария C 3D, заключалось в создании пользовательских макросов new / malloc и delete, которые регистрировали каждое выделение и освобождение файла. Конечно, мы должны были убедиться, что весь код вызывал наши макросы. Запись в файл журнала контролировалась флагом времени выполнения и происходила только при отладке, поэтому нам не приходилось перекомпилировать.
После завершения выполнения постпроцессор перебирал выделенные файлы, соответствующие освобожденным, и сообщал о любых несогласованное выделение.
Это сильно сказалось на производительности, но нам нужно было сделать это только однажды.
Есть ли реальная необходимость в использовании собственного средства обнаружения утечек памяти?
Предполагая, что вы не можете использовать средства проверки динамической памяти, такие как инструмент с открытым исходным кодом valgrind Linux, инструменты статического анализа, такие как коммерческие продукты Coverity Prevent и Klocwork Insight , могут быть полезны. Я использовал все три и получил очень хорошие результаты со всеми из них.
Если вы переопределите конструктор и деструктор своих классов, вы можете печатать на экране или в файле журнала. Это даст вам представление о том, когда что-то создается, что создается, а также ту же информацию для удаления.
Для облегчения просмотра вы можете добавить временную глобальную переменную «INSTANCE_ID» и распечатать это (и увеличивать) при каждом вызове конструктора / деструктора. Затем вы можете просматривать по идентификатору, и это должно немного упростить работу.
http://www.linuxjournal.com/article/6059
На самом деле, исходя из моего опыта, всегда лучше создавать пулы памяти для встроенных систем и использовать собственный распределитель / де-распределитель. Мы легко можем определить утечки. Например, у нас был простой пользовательский менеджер памяти для vxworks, где мы сохраняем идентификатор задачи и временную метку в выделенном блоке памяти.
overloading new
и delete
должны работать, если вы внимательно следите за ним.
Может быть, вы можете показать нам, что не работает в этом подходе ?
Я не эксперт по встроенной среде , поэтому единственный совет, который я могу дать, - это протестировать как можно больше кода на своей машине разработки, используя свои любимые бесплатные или проприетарные инструменты. Также могут существовать инструменты для конкретной встроенной платформы, и вы можете использовать их для окончательного тестирования. Но самые мощные инструменты предназначены для настольных компьютеров.
В среде рабочего стола мне нравится работа DevPartner Studio. Это для Windows и проприетарный. Для Linux доступны бесплатные инструменты, но у меня нет опыта работы с ними. В качестве примера есть EFence
Множество хороших ответов.
Я бы просто отметил, что если программа является такой, которая, как небольшая утилита командной строки, запускается в течение короткого периода времени, а затем освобождает все его память возвращается в ОС, утечки памяти, вероятно, не причинят вреда.