Ответ 1
Вы действительно, действительно, должны прочитать хорошую книгу - обучение С++ откровенно невозможно без него. Я рекомендую Ускоренный С++, Кениг и Му, два из создателей С++.
Я делал все возможное, чтобы выучить С++, но мое предыдущее обучение не оправдало бы одной важной проблемы: управление памятью. У моих основных языков есть автоматическая сборка мусора, поэтому отслеживание всего никогда не было необходимым. Я пробовал читать информацию об управлении памятью в С++ в Интернете, но у меня есть это трясущееся подозрение, что у меня что-то не хватает.
Итак, здесь многочастный вопрос:
delete
для любых новых указателей перед циклом повторно итерацию. Это верно? Нужно ли что-то делать со ссылками?malloc
free
calloc
realloc
*********************** ОБНОВЛЕНИЕ *******************
Это ссылка на ссылку lmgtfy в комментарии один (от Ewan). Если вы начнете читать информацию, доступную там, это не полезно начинающему. По-моему, это великая теория, но она не является ни актуальной, ни полезной для этого вопроса.
Вы действительно, действительно, должны прочитать хорошую книгу - обучение С++ откровенно невозможно без него. Я рекомендую Ускоренный С++, Кениг и Му, два из создателей С++.
_
int* data1 = new int(5);
delete data1;
int* data2 = new int[5];
delete [] data2;
выброс исключений из деструктора
Динамическое выделение массива объектов
Название шаблона для создания в конструкторе, удаление в деструкторе (С++)
Интеллектуальные указатели: или кому принадлежит ребенок?
Каковы принципы, определяющие вашу политику обработки исключений?
// Every new is matched by a delete.
for(int loop = 0;loop < 10;++loop)
{
data = new int(5);
}
delete data;
// The problem is that every 'use of' new is not matched by a delete.
// Here we allocate 10 integers but only release the last one.
class MyArray
{
// Use RAII to manage the dynamic array in an exception safe manor.
public:
MyArray(int size)
:data( new int[size])
{}
~MyArray()
{
delete [] data;
}
// PROBLEM:
// Ignored the rule of 4.
// The compiler will generate a copy constructor and assignment operator.
// These default compiler generated methods just copy the pointer. This will
// lead to double deletes on the memory.
private:
int* data;
};
// Understand what the properties of the smart pointers are:
//
std::vector<std::auto_ptr<int> > data;
// Will not work. You can't put auto_ptr into a standard container.
// This is because it uses move semantics not copy semantics.
// Gurantee that exceptions don't screw up your object:
//
class MyArray
{
// ... As Above: Plus
void resize(int newSize)
{
delete [] data;
data = new int[newSize];
// What happens if this new throws (because there is not enough memory)?
// You have deleted the old data so the old data so it points at invalid memory.
// The exception will leave the object in a completely invalid state
}
Что вам нужно знать об управлении памятью в самом простом смысле, заключается в том, что вам нужно удалить память, которую вы кладете в кучу. Поэтому при создании объекта типа MyClass *myClass = new MyClass(x);
вам нужно иметь какое-то место в вашем коде, которое освобождает/удаляет его с помощью соответствующего delete
. Это кажется простым на практике, но без надлежащего проектирования и использования вспомогательных объектов, таких как общие указатели, это может быстро запутаться, особенно при сохранении кода и добавлении функций. Например, это классическая утечка памяти:
try
{
MyClass *myClass = new MyClass(x);
// Do some stuff can throw an exception
delete myClass;
}
catch(...)
{
// Memory leak on exceptions. Delete is never called
}
ИЛИ другое большое управление памятью вызывает неправильный тип удаления:
int* set = new int[100];
delete set; // Incorrect - Undefined behavior
// delete [] set; is the proper way to delete an array of pointers
Общим способом помочь себе является использование идиомы RAII. (Инициализация ресурсов)
Ниже приведен пример использования библиотеки std для предотвращения утечки памяти:
try
{
auto_ptr<MyClass> myClass(new MyClass(x));
// Now the heap allocated memory associated with myClass
// will automatically be destroyed when it goes out of scope,
// but you can use it much like a regular pointer
myClass->memberFunction();
}
catch (...)
{
}
Более подробную информацию о auto_ptr
можно найти здесь. Если вы можете использовать С++ 11, shared_ptr
является очень рекомендуемым выбором и часто предпочитается над auto_ptr.
Во-первых, вы должны понимать понятия stack и куча.
После вы понимаете эти понятия, переходите к изучению языковых конструкций.
Каков минимальный минимум, который мне нужно знать об управлении памятью? (или, где я могу найти это)?
Для каждого нового должно быть удаление
Где я могу перейти на промежуточное и продвинутое знание/учебники/etc (как только я закончил s > основами)?
Прочитайте эффективный С++, более эффективный С++ и эффективный STL. Затем google (std::) auto_ptr, (boost::) scoped_ptr и (boost::) shared_ptr
В частности: какова разница в производительности между указателями и ссылками?
Я не знаю, с моей точки зрения, поскольку ссылка является копией значения указателя, я не предвижу больших проблем с производительностью.
Я слышал, что в циклах вам нужно убедиться, что вы вызываете delete на любых новых указателях до повторного итерации цикла. Правильно ли это?
Да.
Вам нужно что-то сделать со ссылками?
Нет.
Какие классические примеры утечек памяти?
int * foo() {
...
return new int(...);
}
int main() {
int i = *foo();
...
//the new int() from foo leaks
}
Что мне нужно знать о следующем (и мне когда-либо реально нужно будет использовать их - если да, то где?):
Прежде всего, вы никогда не должны delete
a malloc
ed указатель и никогда free
указатель, созданный с помощью new
. В общем, эти функции не должны появляться в коде С++. Однако, если вы окажетесь на c-land...
malloc: аналогично новому (выделяет память в куче)
бесплатно: аналогично удалению (свободная память в куче)
calloc: Аналогично новому + memset (выделяет память в куче, устанавливает ее на ноль)
realloc: пытается изменить размер блока памяти или создает новый блок-блок памяти и копирует старые данные, free
с помощью старого указателя. Нет реального эквивалента С++.
Некоторая аккуратная память может быть найдена googleing (это то, как написано?) placement new
Вы должны изучить интеллектуальные указатели, они значительно облегчат вашу жизнь, когда дело доходит до управления памятью.
Книга под названием "Память как концепция программирования в C и С++" является очень хорошим для кого-то, кто не знаком с C/С++.
Из вашего списка вы пропустили new
и delete
- некоторые говорят, что никогда не использовать malloc
и free
.
Также забытый delete[]
.
Ничего себе, это много, чтобы справиться.
Самое главное - быть последовательно усердным и дисциплинированным. Это верно для любого ресурса на любом языке, даже более безопасного управляемого языка. Люди чувствуют, что, когда язык управляет их памятью для них, им не нужно об этом думать. Но всегда лучше освобождать любые ресурсы как можно быстрее после того, как вы закончите с ними. Я всегда чувствовал, что "Сбор мусора" заставил программистов лениться в последние годы.
Когда я выделяю память на языке, подобном С++, я должен убедиться, что сначала освобожу его, прежде чем использовать его. Другими словами, я пишу выделение и освобождаю и потом заполняю середину. Важно придерживаться последовательной привычки. Я думаю, что минимальный минимум для изучения... правильного и дисциплинированного управления ресурсами. Это не только память, она должна применяться ко всем ресурсам, включая ссылки на базы данных, ссылки на файлы, дескрипторы контекста и другие подобные животные.
Весь предмет управления памятью на С++ довольно обширен. Я бы сказал, что прочитайте, изучите и код как можно больше.
Пример:
char* myusedmemory;
myusedmemory = (char *)malloc(1000); // allocate memory
free(myusedmemory); // immediately deallocate memory
/* go back and fill in the code between */
Есть много хороших ссылок для получения дополнительных знаний по этому вопросу. Я нашел, что изучение учебников на relisoft.com было для меня полезным, хотя в главном учебнике есть определенная Windows. Еще одна хорошая ссылка может быть найдена здесь.
Что касается различий между указателями и ссылками, одним из основных отличий является гибкость. Вы должны немедленно определить ссылку (int iExample; int & refExample = iExample;) Я бы не думал, что будет большая разница в производительности. Однако указатели, являющиеся более мощными и более гибкими, будут более опасными и потребуют, чтобы вышеупомянутая дисциплина управлялась.
примеры утечек памяти здесь. но вы можете найти больше по googling "утечки памяти в С++"
Что касается malloc, free, calloc, realloc, это просто функции, как и любые другие команды, в этих конкретных случаях, функции, включенные в stdlib. Вам просто нужно понять, что они делают и как их использовать, так же, как и с любыми другими функциями, так же как и обычный printf().
как примечание: Умные указатели - очень хороший способ пойти и, как правило, безопаснее.
как еще одно примечание, я хотел упомянуть Code Complete, лучшую книгу, которую я прочитал по теме управления ресурсами. Я читал его, чтобы покрыть много, много раз.
В других языках вам уже необходимо отслеживать подключения к базе данных, дескрипторы окон, сокеты и т.д. с помощью таких механизмов, как "finally" (в Java) или "использование" (в С#). В С++ просто добавьте память в этот список. Это действительно не принципиально иное.
Здесь что-то часто ловит студентов: большие, действительно большие объекты, такие как массивы, должны выделяться в динамической памяти (т.е. с помощью new
).
Кроме того, не обходите большие объекты. Пропускайте указатели, предпочтительные интеллектуальные указатели, к объектам. Копирование больших объектов требует много процессорного времени.
Настроить и документировать правила размещения и владения объектами. Получает ли вызывающий или вызывающий объект?
Не возвращать ссылки на локальные объекты и указатели на локальные объекты.
Узнайте о RAII. Некоторые люди здесь указали это, но в то же время объяснили новый/удаленный материал, не подчеркивая важность RAII. С RAII вам не нужно беспокоиться об управлении памятью.
Люди, новые для С++, имеют тенденцию кодировать, как в Java, ставя "новый" везде. Во многих случаях это плохая привычка, в некоторых случаях вы не можете ее избежать (но из опыта моих проектов это, скорее всего, никогда).
Просто добавив этот комментарий, чтобы подчеркнуть это;) Однако все комментарии совершенно правильные.
new
и delete
- два наиболее важных ключевых слова для управления памятью. И в самом простом случае вам просто нужно запомнить вызов delete
для каждого объекта, который вы называете new
on. Поэтому, если вы вызываете new
в цикле, вам нужно убедиться, что вы вызываете delete
для каждого из этих new
'ed объектов. Вам не нужно делать это из цикла, пока вы сохраняете копию каждого указателя где-нибудь, который может быть удален позже.
malloc
, free
, calloc
и realloc
, вероятно, более продвинуты, чем вам нужно беспокоиться. Я просто помню, что они есть, если стандартный new
/delete
когда-либо чувствует себя ограниченным.
Что все сказано, умные указатели могут быть большой помощью, но иногда полезно знать, как делать вещи трудным путем, прежде чем решать умные указатели.
Поскольку я изучал С++, я обнаружил, что использование инструмента анализа памяти, такого как Valgrind, незаменим для помощи найти утечки памяти. Когда вы запускаете свою программу (скомпилированную с помощью символов отладки) из Valgrind, она будет идентифицировать строки, в которых выделена память, но не будет освобождена позже.
Я использую эти аргументы командной строки:
valgrind --leak-check=yes --num-callers=8 ./myExecutable
Обратите внимание, что ваша программа будет работать намного медленнее, чем при самостоятельной работе, но она часто стоит усилий.
Все упоминают новое и удаляют, но большую часть времени вам не нужны и не должны использовать их явно:
Конечно, по соображениям производительности вы можете иметь случайную новую и удаляемую пару в критическом состоянии, но это должно быть исключение, а не правило.