Что мне нужно знать о памяти на С++?

Я делал все возможное, чтобы выучить С++, но мое предыдущее обучение не оправдало бы одной важной проблемы: управление памятью. У моих основных языков есть автоматическая сборка мусора, поэтому отслеживание всего никогда не было необходимым. Я пробовал читать информацию об управлении памятью в С++ в Интернете, но у меня есть это трясущееся подозрение, что у меня что-то не хватает.

Итак, здесь многочастный вопрос:

  • Каков минимальный минимум, который мне нужно знать об управлении памятью? (или, где я могу найти это)?
  • Где я могу найти промежуточные и продвинутые знания/учебники/и т.д. (как только я закончил с основными)?
    В частности:
  • Какова разница в производительности между указателями и ссылками?
  • Я слышал, что в циклах вам нужно убедиться, что вы вызываете delete для любых новых указателей перед циклом повторно итерацию. Это верно? Нужно ли что-то делать со ссылками?
  • Какие классические примеры утечек памяти?
  • Что мне нужно знать о следующем (и мне когда-либо реально нужно будет использовать их - если да, то где?):
    • malloc
    • free
    • calloc
    • realloc
    литий >

*********************** ОБНОВЛЕНИЕ *******************

Это ссылка на ссылку lmgtfy в комментарии один (от Ewan). Если вы начнете читать информацию, доступную там, это не полезно начинающему. По-моему, это великая теория, но она не является ни актуальной, ни полезной для этого вопроса.

Ответы

Ответ 1

Вы действительно, действительно, должны прочитать хорошую книгу - обучение С++ откровенно невозможно без него. Я рекомендую Ускоренный С++, Кениг и Му, два из создателей С++.

Ответ 2

Управление памятью

Основы

  • Каждое использование "new" должно соответствовать "использованию" delete
  • Array new отличается от обычного нового и имеет свой собственный

_

 int*  data1 = new int(5);
 delete data1;

 int*  data2 = new int[5];
 delete [] data2;

Знать

  • Исключения
  • RAII
  • Правило 4.

выброс исключений из деструктора
Динамическое выделение массива объектов Название шаблона для создания в конструкторе, удаление в деструкторе (С++)

Лучшая практика

  • Никогда не используйте RAW-указатели.
  • Всегда указывайте указатели в Smart Pointers.
  • Изучите различные типы интеллектуальных указателей и когда использовать каждый

Интеллектуальные указатели: или кому принадлежит ребенок?

Дополнительно:

  • Общие сведения о гарантиях исключений
  • Понимание использования предложения throw

Каковы принципы, определяющие вашу политику обработки исключений?

Общие способы утечки

Основы

// 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
   }

Ответ 3

Что вам нужно знать об управлении памятью в самом простом смысле, заключается в том, что вам нужно удалить память, которую вы кладете в кучу. Поэтому при создании объекта типа 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.

Ответ 4

Во-первых, вы должны понимать понятия stack и куча.

После вы понимаете эти понятия, переходите к изучению языковых конструкций.

Ответ 5

Каков минимальный минимум, который мне нужно знать об управлении памятью? (или, где я могу найти это)?

Для каждого нового должно быть удаление

Где я могу перейти на промежуточное и продвинутое знание/учебники/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

Ответ 7

Книга под названием "Память как концепция программирования в C и С++" является очень хорошим для кого-то, кто не знаком с C/С++.

Ответ 8

Из вашего списка вы пропустили new и delete - некоторые говорят, что никогда не использовать malloc и free.

Также забытый delete[].

Ответ 9

Ничего себе, это много, чтобы справиться.

Самое главное - быть последовательно усердным и дисциплинированным. Это верно для любого ресурса на любом языке, даже более безопасного управляемого языка. Люди чувствуют, что, когда язык управляет их памятью для них, им не нужно об этом думать. Но всегда лучше освобождать любые ресурсы как можно быстрее после того, как вы закончите с ними. Я всегда чувствовал, что "Сбор мусора" заставил программистов лениться в последние годы.

Когда я выделяю память на языке, подобном С++, я должен убедиться, что сначала освобожу его, прежде чем использовать его. Другими словами, я пишу выделение и освобождаю и потом заполняю середину. Важно придерживаться последовательной привычки. Я думаю, что минимальный минимум для изучения... правильного и дисциплинированного управления ресурсами. Это не только память, она должна применяться ко всем ресурсам, включая ссылки на базы данных, ссылки на файлы, дескрипторы контекста и другие подобные животные.

Весь предмет управления памятью на С++ довольно обширен. Я бы сказал, что прочитайте, изучите и код как можно больше.

Пример:

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, лучшую книгу, которую я прочитал по теме управления ресурсами. Я читал его, чтобы покрыть много, много раз.

Ответ 10

В других языках вам уже необходимо отслеживать подключения к базе данных, дескрипторы окон, сокеты и т.д. с помощью таких механизмов, как "finally" (в Java) или "использование" (в С#). В С++ просто добавьте память в этот список. Это действительно не принципиально иное.

Ответ 11

Здесь что-то часто ловит студентов: большие, действительно большие объекты, такие как массивы, должны выделяться в динамической памяти (т.е. с помощью new).

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

Настроить и документировать правила размещения и владения объектами. Получает ли вызывающий или вызывающий объект?

Не возвращать ссылки на локальные объекты и указатели на локальные объекты.

Ответ 12

Узнайте о RAII. Некоторые люди здесь указали это, но в то же время объяснили новый/удаленный материал, не подчеркивая важность RAII. С RAII вам не нужно беспокоиться об управлении памятью.

Люди, новые для С++, имеют тенденцию кодировать, как в Java, ставя "новый" везде. Во многих случаях это плохая привычка, в некоторых случаях вы не можете ее избежать (но из опыта моих проектов это, скорее всего, никогда).

Просто добавив этот комментарий, чтобы подчеркнуть это;) Однако все комментарии совершенно правильные.

Ответ 13

new и delete - два наиболее важных ключевых слова для управления памятью. И в самом простом случае вам просто нужно запомнить вызов delete для каждого объекта, который вы называете new on. Поэтому, если вы вызываете new в цикле, вам нужно убедиться, что вы вызываете delete для каждого из этих new 'ed объектов. Вам не нужно делать это из цикла, пока вы сохраняете копию каждого указателя где-нибудь, который может быть удален позже.

malloc, free, calloc и realloc, вероятно, более продвинуты, чем вам нужно беспокоиться. Я просто помню, что они есть, если стандартный new/delete когда-либо чувствует себя ограниченным.

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

Ответ 14

Поскольку я изучал С++, я обнаружил, что использование инструмента анализа памяти, такого как Valgrind, незаменим для помощи найти утечки памяти. Когда вы запускаете свою программу (скомпилированную с помощью символов отладки) из Valgrind, она будет идентифицировать строки, в которых выделена память, но не будет освобождена позже.

Я использую эти аргументы командной строки:

 valgrind --leak-check=yes --num-callers=8 ./myExecutable

Обратите внимание, что ваша программа будет работать намного медленнее, чем при самостоятельной работе, но она часто стоит усилий.

Ответ 15

Все упоминают новое и удаляют, но большую часть времени вам не нужны и не должны использовать их явно:

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

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