Существует два широко используемых метода выделения памяти: автоматическое распределение и динамическое распределение.Обычно для каждой области памяти имеется соответствующая область: стек и куча.
Стек
Стек всегда выделяет память последовательно.Это может быть сделано, потому что это требует от вас освобождения памяти в обратном порядке (первый вход, последний выход: FILO).Это метод выделения памяти для локальных переменных во многих языках программирования.Это очень, очень быстро, потому что требует минимального учета, а следующий адрес для выделения неявен.
В C ++ это называется автоматическое хранение , поскольку хранение запрашивается автоматически в концеобъем.Как только выполнение текущего блока кода (разделенного с помощью {}
) завершено, память для всех переменных в этом блоке автоматически собирается.Это также момент, когда деструкторы вызываются для очистки ресурсов.
Куча
Куча обеспечивает более гибкий режим выделения памяти.Бухгалтерия сложнее, а распределение медленнее.Поскольку нет неявной точки освобождения, вы должны освободить память вручную, используя delete
или delete[]
(free
в C).Однако отсутствие неявной точки освобождения является ключом к гибкости кучи.
Причины использования динамического выделения
Даже если использование кучи медленнее и потенциально может привести к утечкам памяти или фрагментации памятиЕсть очень хорошие варианты использования для динамического выделения, поскольку оно менее ограничено.
Две основные причины использования динамического выделения:
Вы не знаете, сколько памятивам нужно во время компиляции.Например, при чтении текстового файла в строку вы обычно не знаете, какой размер у файла, поэтому вы не можете решить, какой объем памяти выделить, пока не запустите программу.
Вы хотите выделить память, которая будет сохраняться после выхода из текущего блока.Например, вы можете написать функцию string readfile(string path)
, которая возвращает содержимое файла.В этом случае, даже если в стеке может храниться все содержимое файла, вы не сможете вернуться из функции и сохранить выделенный блок памяти.
Почему динамическое выделение часто не требуется
В C ++ есть аккуратная конструкция, называемая деструктором .Этот механизм позволяет вам управлять ресурсами путем выравнивания времени жизни ресурса с временем жизни переменной.Эта техника называется RAII и является отличительной чертой C ++.Он «оборачивает» ресурсы в объекты.std::string
является прекрасным примером.Этот фрагмент:
int main ( int argc, char* argv[] )
{
std::string program(argv[0]);
}
фактически выделяет переменный объем памяти.Объект std::string
выделяет память с помощью кучи и освобождает ее в своем деструкторе.В этом случае вам не нужно было вручную управлять любыми ресурсами, но вы все равно получили преимущества динамического выделения памяти.
В частности, это означает, что в этом фрагменте:
int main ( int argc, char* argv[] )
{
std::string * program = new std::string(argv[0]); // Bad!
delete program;
}
нет необходимости динамического выделения памяти.Программа требует большего набора текста (!) И вводит риск забыть освободить память.Это делает это без видимой выгоды.
Почему вы должны использовать автоматическое хранение как можно чаще
По сути, последний абзац подводит итог.Использование автоматического хранения как можно чаще делает ваши программы:
- быстрее для ввода;
- быстрее при запуске;
- менее подвержены утечкам памяти / ресурсов.
Бонусные баллы
В указанном вопросе есть дополнительные проблемы.В частности, следующий класс:
class Line {
public:
Line();
~Line();
std::string* mString;
};
Line::Line() {
mString = new std::string("foo_bar");
}
Line::~Line() {
delete mString;
}
На самом деле использовать намного опаснее, чем следующий:
class Line {
public:
Line();
std::string mString;
};
Line::Line() {
mString = "foo_bar";
// note: there is a cleaner way to write this.
}
Причина в том, что std::string
правильно определяет конструктор копирования,Рассмотрим следующую программу:
int main ()
{
Line l1;
Line l2 = l1;
}
При использовании оригинальной версии эта программа, вероятно, завершится сбоем, так как она использует delete
в одной строке дважды.Используя измененную версию, каждый экземпляр Line
будет иметь собственную строку экземпляр , каждый со своей памятью, и оба будут выпущены в конце программы.
Другие примечания
Широкое использование RAII считается лучшей практикой в C ++ по всем вышеуказанным причинам.Однако есть дополнительное преимущество, которое не сразу очевидно.В принципе, это лучше, чем сумма его частей.Весь механизм составляет .Он масштабируется.
Если вы используете класс Line
в качестве строительного блока:
class Table
{
Line borders[4];
};
Тогда
int main ()
{
Table table;
}
выделяет четыре std::string
экземпляра, четыре Line
экземпляра, один Table
экземпляр и все содержимое строки и все автоматически освобождается .