Указатель NULL
- это тот, который указывает на никуда. Когда вы разыскиваете указатель p
, вы говорите «дайте мне данные в месте, хранящемся в« p ». Когда p
является нулевым указателем, местоположение, хранящееся в p
, является nowhere
, вы говорите «Дайте мне данные в месте« нигде ». Очевидно, он не может этого сделать, поэтому он выбрасывает NULL pointer exception
.
В общем, это потому, что что-то не было правильно инициализировано.
Вместо руководящей памяти вручную, попытайтесь использовать интеллектуальные указатели когда это применимо.
Смотрят на lib Повышения , TR1, и интеллектуальные указатели .
Также интеллектуальные указатели являются теперь частью стандарта C++, названного C++ 11 .
Точно один возврат из любой функции. Тем путем можно сделать освобождение там и никогда не пропускать его.
слишком легко сделать ошибку иначе:
new a()
if (Bad()) {delete a; return;}
new b()
if (Bad()) {delete a; delete b; return;}
... // etc.
C++ разработан RAII в памяти. Нет действительно никакого лучшего способа управлять памятью в C++, я думаю. Но бойтесь выделять очень большие блоки (как буферные объекты) на локальном объеме. Это может вызвать переполнения стека и, если существует дефект в границах, проверяющих при использовании того блока, можно перезаписать другие переменные или обратные адреса, который приводит ко всем дырам в системе безопасности видов.
Управляйте памятью тем же путем, Вы управляете другими ресурсами (дескрипторы, файлы, соединения дб, сокеты...). GC не помог бы Вам с ними также.
Одним из единственных примеров о выделении и уничтожении в различных местах является создание потока (параметр, который Вы передаете). Но даже в этом случае легко. Вот функция/метод, создающая поток:
struct myparams {
int x;
std::vector<double> z;
}
std::auto_ptr<myparams> param(new myparams(x, ...));
// Release the ownership in case thread creation is successfull
if (0 == pthread_create(&th, NULL, th_func, param.get()) param.release();
...
Здесь вместо этого функция потока
extern "C" void* th_func(void* p) {
try {
std::auto_ptr<myparams> param((myparams*)p);
...
} catch(...) {
}
return 0;
}
Симпатичный easyn не так ли? В случае, если создание потока перестало работать, ресурс будет free'd (удаленным) auto_ptr, иначе владение будет передано потоку. Что, если поток так быстр, что после создания он выпускает ресурс перед
param.release();
, называют в основной функции/методе?Ничего! Поскольку мы 'скажем' auto_ptr игнорировать освобождение. Действительно ли управление памятью C++ легко не так ли? С наилучшими пожеланиями,
Ema!
Мы переносим все наши функции выделения со слоем, который добавляет краткую строку в передней стороне и флаг сигнальной метки в конце. Так, например, у Вас был бы вызов к "myalloc (pszSomeString, iSize, iAlignment); или новый ("описание", iSize) MyObject (); который внутренне выделяет указанный размер плюс достаточно пространства для Вашего заголовка и сигнальной метки. Конечно, не забывайте комментировать это для сборок неотладки! Требуется немного больше памяти, чтобы сделать это, но преимущества явно перевешивают затраты.
Это обладает тремя преимуществами - сначала это позволяет Вам легко, и быстро отследите то, что код пропускает путем выполнения быстрых поисков кода, выделенного в определенных 'зонах', но не очищенный, когда те зоны должны были освободить. Может также быть полезно обнаружить, когда граница была перезаписана путем проверки, чтобы гарантировать, что все сигнальные метки неповреждены. Это сохранило нас многочисленные времена при попытке найти, что те хорошо скрытые отказывают или оплошности массива. Третье преимущество находится в отслеживании использования памяти для наблюдения, кто крупные игроки - сопоставление определенных описаний в MemDump говорит Вам, когда 'звук' поднимает путь больше пространства, чем Вы ожидали, например.
Можно прервать функции выделения памяти и видеть, существуют ли некоторые зоны памяти, не освобожденные на выход программы (хотя это не подходит для весь приложения).
Это может также быть сделано во время компиляции путем замены новых операторов и удалить и другие функции выделения памяти.
, Например, регистрация этого сайт [Отладка выделения памяти в C++] Примечание: существует прием для оператора delete также что-то вроде этого:
#define DEBUG_DELETE PrepareDelete(__LINE__,__FILE__); delete
#define delete DEBUG_DELETE
можно сохранить в некоторых переменных название файла и когда перегруженный оператор delete будет знать, который был местом, от которого это назвали. Таким образом, у Вас может быть трассировка каждого удаляющий и malloc из Вашей программы. В конце последовательности проверки памяти необходимо смочь сообщить, какой выделенный блок памяти не был 'удален', определив его именем файла и номером строки, который является, я предполагаю то, что Вы хотите.
Вы могли также попробовать что-то как BoundsChecker в соответствии с Visual Studio, которая довольно интересна и проста в использовании.
valgrind (только польза для *отклоняет платформы), очень хорошее средство проверки памяти
Если Вы собираетесь управлять своей памятью вручную, у Вас есть два случая:
, Если необходимо нарушить какое-либо из этих правил, зарегистрируйте его.
Это - все о владении указателя.
Для MSVC только, добавьте следующее к вершине каждого .cpp файла:
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
Затем при отладке с VS2003 или больше, Вам скажут о любых утечках, когда Ваша программа выйдет (это отслеживает новый/удаляющий). Это является основным, но это помогло мне в прошлом.
Другие упомянули способы избежать утечек памяти во-первых (как интеллектуальные указатели). Но инструмент профилирования и анализа памяти является часто единственным способом разыскать проблемы памяти, после того как у Вас есть они.
Valgrind memcheck является превосходным свободным.
Если Вы можете, использовать повышение shared_ptr и стандартный C++ auto_ptr. Они передают семантику владения.
при возврате auto_ptr Вы говорите вызывающей стороне, что даете им владение памяти.
при возврате shared_ptr Вы говорите вызывающей стороне, что у Вас есть ссылка на него, и они принимают участие владения, но это не только их ответственность.
Они семантика также относятся к параметрам. Если вызывающая сторона передает Вас auto_ptr, они дают Вам владение.
Частый источник этих ошибок - когда у Вас есть метод, который принимает ссылку или указатель на объект, но оставляет владение неясным. Разработайте и комментарий, что конвенции могут сделать это менее вероятно.
Позволяют случаю, где функция берет владение объекта быть особым случаем. Во всех ситуациях, где это происходит, убедиться записать комментарий рядом с функцией в заголовочном файле, указывающем на это. Необходимо стремиться удостовериться, что в большинстве случаев модуль или класс, который выделяет объект, также ответственны за освобождение его.
Используя константу может помочь много в некоторых случаях. Если функция не изменит объект и не хранит ссылку на него, которая сохраняется после того, как он возвращается, примите ссылку константы. От чтения кода вызывающей стороны будет очевидно, что Ваша функция не приняла владение объекта. У Вас, возможно, была та же функция, принимают указатель неконстанты, и вызывающая сторона может или не могла предположить, что вызываемый принял владение, но со ссылкой константы нет никакого вопроса.
не используют ссылки неконстанты в списках аргументов. Это очень неясно при чтении кода вызывающей стороны, что вызываемый, возможно, сохранил ссылку на параметр.
я не соглашаюсь с комментариями, рекомендующими ссылку считаемые указатели. Это обычно хорошо работает, но когда у Вас есть ошибка, и она не работает, особенно если Ваш деструктор делает что-то нетривиальное, такой как в многопоточной программе. Определенно попытайтесь скорректировать свой дизайн для не необходимости в подсчете ссылок, если это не слишком твердо.
Подсказки в порядке Важности:
-Tip#1 Всегда не забывают объявлять Ваши "виртуальные" деструкторы.
smartpointers
-Tip#4 повышения Использования RAII
-Tip#3 Использования-Tip#2 не пишет Ваш собственный ошибочный Smartpointers, использует повышение (на проекте, я иду прямо сейчас, я не могу использовать повышение, и я перенес необходимость отладить мои собственные интеллектуальные указатели, я определенно не следовал бы тем же маршрутом снова, но с другой стороны прямо сейчас я не могу добавить повышение наших зависимостей)-Tip#5, Если некоторые случайные / очень важное невыполнение (как в играх с тысячами объектов) взгляд работы на контейнер указателя повышения Thorsten Ottosen
-Tip#6 Находят заголовок обнаружения утечки для Вашей предпочтительной платформы, такой как "vld" заголовок Обнаружения Визуальной Утечки
Кроме того, не используйте вручную выделенную память, если существует класс библиотеки станд. (например, вектор). Удостоверьтесь, нарушаете ли Вы то правило, что у Вас есть виртуальный деструктор.
Я полностью подтверждаю весь совет о RAII и интеллектуальных указателях, но я также хотел бы добавить немного высокоуровневую подсказку: самая легкая память для управления является памятью, которую Вы никогда не выделяли. В отличие от языков как C# и Java, где в значительной степени все - ссылка в C++, необходимо поместить объекты на стек каждый раз, когда Вы можете. Поскольку я имею, видят, что несколько человек (включая доктора Stroustrup) указывают, главная причина, почему сборка "мусора" никогда не была популярна в C++, состоит в том, что правильно написанный C++ не производит большого количества мусора во-первых.
не пишут
Object* x = new Object;
или даже
shared_ptr<Object> x(new Object);
, когда можно просто записать
Object x;
Читайте на RAII и удостоверьтесь, что Вы понимаете это.
Вы захотите посмотреть на интеллектуальные указатели, такой как интеллектуальные указатели повышения .
Вместо
int main()
{
Object* obj = new Object();
//...
delete obj;
}
повышение:: shared_ptr автоматически удалит, после того как подсчет ссылок является нулем:
int main()
{
boost::shared_ptr<Object> obj(new Object());
//...
// destructor destroys when reference count is zero
}
Примечание мое последнее примечание, "когда подсчет ссылок является нулем, который является самой прохладной частью. Таким образом, Если у Вас есть многочисленные пользователи Вашего объекта, Вы не должны будете отслеживать то, используется ли объект все еще. После того как никто не обращается к Вашему общему указателю, он уничтожается.
Это не панацея, как бы то ни было. Хотя можно получить доступ к указателю базы, Вы не хотели бы передавать его третьей стороне API, если Вы не были уверены относительно того, что он делал. Много времен, Вашего материала "регистрации" к некоторому другому потоку для работы, которая будет сделана ПОСЛЕ ТОГО, КАК объем создания закончен. Это распространено с PostThreadMessage в Win32:
void foo()
{
boost::shared_ptr<Object> obj(new Object());
// Simplified here
PostThreadMessage(...., (LPARAM)ob.get());
// Destructor destroys! pointer sent to PostThreadMessage is invalid! Zohnoes!
}
Как всегда, используйте свое думающее ограничение с любым инструментом...
Это сообщение, кажется, является повторяющимся, но в C++, самый основной шаблон для знания RAII.
Учатся использовать интеллектуальные указатели, обоих от повышения, TR1 или даже непритязательного (но часто достаточно эффективный) auto_ptr (но необходимо знать его ограничения).
RAII является основанием и безопасности исключения и распоряжения ресурса в C++, и никакой другой шаблон (тестовая система, и т.д.) не даст Вам обоим (и большую часть времени, это не даст Вам ни один).
Посмотрите ниже сравнения RAII и не кода RAII:
void doSandwich()
{
T * p = new T() ;
// do something with p
delete p ; // leak if the p processing throws or return
}
void doRAIIDynamic()
{
std::auto_ptr<T> p(new T()) ; // you can use other smart pointers, too
// do something with p
// WON'T EVER LEAK, even in case of exceptions, returns, breaks, etc.
}
void doRAIIStatic()
{
T p ;
// do something with p
// WON'T EVER LEAK, even in case of exceptions, returns, breaks, etc.
}
Для суммирования (после комментария от [1 124] людоед Psalm33 ) RAII полагается на три понятия:
Это означает, что в корректном коде C++, большинство объектов не будет создано с new
и будет объявлено на стеке вместо этого. И для те созданное использование new
, все будут так или иначе , определил объем (например, присоединил к интеллектуальному указателю).
Как разработчик, это очень мощно действительно, поскольку Вы не должны будете заботиться о ручной обработке ресурса (как сделано в C, или для некоторых объектов в Java, который делает интенсивное использование try
/ finally
для того случая)...
"ограниченные по объему объекты... будет разрушено..., неважно, выход", это не совсем верно. существуют способы обмануть RAII., любой аромат оконечных () обойдет очистку. выход (EXIT_SUCCESS) является оксюмороном в этом отношении.
†“ wilhelmtell
wilhelmtell совершенно правилен об этом: существуют исключительные способы обмануть RAII, все ведущие к процессу резкая остановка.
Это исключительно пути, потому что код C++ не замусорен оконечным, выходом, и т.д., или в случае с исключениями, мы действительно хотим необработанное исключение разрушить процесс и дамп ядра его отображение памяти, как, и не после очистки.
, Но мы должны все еще знать о тех случаях, потому что, в то время как они редко происходят, они могут все еще произойти.
(кто звонит terminate
или exit
в случайном коде C++?... Я не забываю иметь необходимость иметь дело с той проблемой при проигрывании с [1 113] ИЗБЫТОК : Этой библиотекой является очень C-oriented, идя до активной разработки его для создания вещей трудными для разработчиков C++ как не забота о приблизительно [1 114], стек выделил данные , или наличие "интересных" решений приблизительно [1 115] никогда возврат из их основного цикла ... Я не прокомментирую об этом) .
Большинство утечек памяти является результатом не соглашения с монопольным использованием объекта и время жизни.
первое, что нужно сделать состоит в том, чтобы выделить на Стеке каждый раз, когда Вы можете. Это имеет дело с большинством случаев, где необходимо выделить отдельный объект для некоторой цели.
при необходимости к 'новому' в объекте затем большую часть времени, он будет иметь единственного очевидного владельца для остальной части его времени жизни. Для этой ситуации я склонен использовать набор шаблонов наборов, которые разработаны для 'владения' объектами, хранившими в них указателем. Они реализованы с вектором STL и контейнерами карты, но имеют некоторые различия:
Мой beaf с STL - то, что он так фокусируется на объектах Значения, в то время как в большинстве объектов приложения уникальные объекты, которым не потребовали значимой семантики копии для использования в тех контейнерах.
Если Вы можете/, используют интеллектуальный указатель для чего-то (хотя это должно быть огромным красным флагом), введите в своем коде с:
allocate
if allocation succeeded:
{ //scope)
deallocate()
}
Это очевидно, но удостоверьтесь, что Вы вводите его прежде , Вы вводите любой код в объеме
Существует уже много о том, как не протечь, но если Вам нужен инструмент для помощи, Вы отследить утечки смотрите на:
Одна техника, которая стала популярной у управления памятью в C++, RAII. В основном Вы используете конструкторов/деструкторы для обработки распределения ресурсов. Конечно, существуют некоторые другие неприятные детали в C++ из-за безопасности исключения, но основная идея довольно проста.
проблема обычно сводится к одному из владения. Я настоятельно рекомендую чтение Эффективного ряда C++ Scott Meyers и современного Дизайна C++ Andrei Alexandrescu.
Пользовательские интеллектуальные указатели везде Вы можете! Просто уходят целые классы утечек памяти.
Вот еще, Вы маленькие дети и Ваши новомодные сборщики "мусора"...
Очень сильные правила о "владении" - какой объект или часть программного обеспечения имеют право удалить объект. Четкие комментарии и мудрые имена переменной для создания его очевидным, если указатель "владеет" или является "просто взглядом, не затрагивайте". Чтобы помочь решить, кто владеет что, следуйте как можно больше за "тестовым" шаблоном в каждой подпрограмме или методе.
create a thing
use that thing
destroy that thing
Иногда необходимо создать и уничтожить в широко различных местах; я думаю трудно для предотвращения этого.
В любой программе, требующей сложных структур данных, я создаю строгое ясное дерево объектов, содержащих другие объекты - использование указателей "владельца". Этот древовидные модели основная иерархия понятий домена приложения. Пример 3D сцена владеет объектами, световыми сигналами, структурами. В конце рендеринга, когда программа выходит, существует ясный способ уничтожить все.
Много других указателей определяются по мере необходимости каждый раз, когда один объект нуждается в доступе другой, для сканирования по массивам или безотносительно; это "просто взгляд". Для 3D примера сцены - объект использует структуру, но не владеет; другие объекты могут использовать ту же самую структуру. Разрушение объекта делает не , вызывают разрушение любых структур.
Да это является трудоемким, но это - то, что я делаю. У меня редко есть утечки памяти или другие проблемы. Но затем я работаю на ограниченной арене высокоэффективного научного, сбора данных и графического программного обеспечения. Я не часто имею дело транзакции как с банковским делом и электронной коммерцией, событийно-ориентированными графический интерфейсами пользователя или высоко объединил асинхронный хаос в сеть. Возможно, новомодные пути имеют преимущество там!
Совместно используйте и знайте правила владения памяти через свой проект. Используя COM правила делают для лучшей непротиворечивости ([в] параметрах, принадлежат вызывающей стороне, вызываемый должен скопировать; параметрические усилители принадлежат вызывающей стороне, вызываемый должен сделать копию при хранении ссылки; и т.д.)
valgrind является хорошим инструментом для проверки утечек памяти программ во времени выполнения, также.
Это доступно на большинстве разновидностей Linux (включая Android) и на Darwin.
, Если Вы используете для записи модульных тестов на программы, необходимо систематически привыкать выполнять valgrind на тестах. Это потенциально избежит многих утечек памяти на ранней стадии. Также обычно легче точно определить их в простых тестах это в полном программном обеспечении.
, Конечно, этот совет остаются допустимыми для любого другого инструмента проверки памяти.
Большой вопрос!
при использовании C++ и Вы разрабатываете ЦП-и-память в реальном времени boud приложение (как игры), необходимо записать собственного Диспетчера памяти.
я думаю лучше, можно сделать, объединить некоторые интересные работы различных авторов, я могу дать Вам некоторую подсказку:
средство выделения Фиксированного размера в большой степени обсуждено, везде в сети
, Маленькое Объектное Выделение было представлено Alexandrescu в 2001 в его идеальной книге "современный дизайн C++"
А, большое продвижение (с распределенным исходным кодом) может быть найдено в удивительной статье в Игровом Драгоценном камне Программирования 7 (2008) названным "Высокопроизводительное средство выделения "кучи"", записанное Dimitar Lazarov
А, которым большой список ресурсов может быть найден в этот , статья
не начинает писать новичку неполезное средство выделения собой... ДОКУМЕНТ САМОСТОЯТЕЛЬНО сначала.