Стек против кучи для фиксированного количества объектов, требующих глобальной области видимости

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

Я понимаю, что куча, доступ к которой осуществляется с помощью оператора «новый», используется для динамического распределения памяти. Согласно ответу на другой вопрос о переполнении стека, «куча предназначена для хранения данных, время жизни которых не может быть определено заранее». Стек быстрее, чем куча, и, похоже, используется для переменных локальной области видимости, т.е. переменные автоматически удаляются, когда соответствующий раздел кода завершается. Стек также имеет относительно ограниченный объем доступного пространства.

В моем случае до времени выполнения я знал, что мне понадобится массив указателей ровно на 500 объектов определенного класса, и я знаю, что мне нужно будет хранить указатели и объекты на протяжении всего времени выполнения. Куча не имеет смысла, потому что я заранее знаю, как долго мне понадобится память, и я точно знаю, какие объекты man мне понадобятся. Стек также не имеет смысла, если он ограничен по объему; плюс, я не знаю, действительно ли он может содержать все мои объекты / указатели.

Как лучше всего подойти к этой ситуации и почему? Спасибо!


person Anthony    schedule 09.02.2012    source источник
comment
Разговоры обо всей этой чепухе со стеками и кучей полностью лишили вас возможности увидеть существование статического хранилища, в которое, вероятно, и должны отправляться ваши данные. Подумайте о статическом, динамическом, автоматическом, когда речь идет о классах хранения, и о постоянном, ручном, автоматическом для соответствующего времени жизни объекта (приблизительно).   -  person Kerrek SB    schedule 09.02.2012
comment
Слово, Керрек. Я действительно хочу, чтобы вся терминология стека / кучи просто умерла своей ужасной смертью и оставила нас навсегда ...   -  person Blindy    schedule 09.02.2012


Ответы (7)


Объекты, размещенные в стеке в main(), имеют время жизни всего выполнения программы, так что это вариант. Массив из 500 указателей составляет либо 2000, либо 4000 байтов в зависимости от того, имеют ли ваши указатели ширину 32 или 64 бита - если бы вы программировали в среде, предел стека которой был настолько мал, вы бы это знали (такие среды do < / em> существует: например, стеки режима ядра часто имеют размер 8192 байта или меньше всего), поэтому я без колебаний поместил бы туда массив.

В зависимости от размера ваших объектов, также может быть разумным поместить их в стек - типичное ограничение стека в пользовательском пространстве в настоящее время составляет порядка 8 мегабайт, что не настолько велик, чтобы его можно было полностью игнорировать, но это тоже не арахис.

Если они слишком велики для стека, я бы серьезно подумал о создании глобальной переменной, которая была бы массивом самих объектов. Основным недостатком этого является то, что вы не можете точно контролировать, когда они инициализируются. Если у объектов есть нетривиальные конструкторы, это может стать проблемой. Альтернативой является выделение хранилища для объектов в качестве глобальной переменной, их инициализация в соответствующей точке в main, используя размещение new, и явный вызов их деструкторов на выходе. Это требует осторожности при наличии исключений; Я бы написал одноразовый класс RAII, инкапсулирующий задание.

person zwol    schedule 09.02.2012
comment
Спасибо за подробный ответ. - person Anthony; 10.02.2012

Это не вопрос стека или кучи (что, если быть точным, не означает то, что вы думаете о С ++: это просто структуры данных, такие как вектор, набор или очередь). Это вопрос продолжительности хранения.

Скорее всего, вам понадобятся здесь объекты статической продолжительности, которые могут быть как глобальными, так и членами класса. Автоматические переменные, объявленные внутри функции main, также могут выполнять эту работу, если вы спроектируете способ доступа к ним из другого кода.

Здесь есть некоторая информация о различных сроках хранения C ++ (автоматическая, статическая, динамическая) < / а>. Однако в принятом ответе используется запутанная терминология стека / кучи, но объяснение правильное.

person Alexandre C.    schedule 09.02.2012

куча предназначена для хранения данных, где время жизни хранилища не может быть определено заранее

Хотя это правильно, это также неполно.

Стек раскручивается, когда вы выходите из его области видимости, поэтому использовать его для переменных с глобальной областью видимости невозможно, как вы сказали. Однако именно здесь вы перестаете быть на правильном пути. Хотя вы знаете время жизни (или, точнее, область действия, поскольку это наиболее важный фактор здесь), вы также знаете, что она выше кадра стека, поэтому, имея только два варианта выбора, вы положил в кучу.

Существует третий вариант - фактическая статическая переменная, объявленная в верхней области видимости, но она будет работать только в том случае, если ваши объекты имеют конструкторы по умолчанию.

TL; DR: использовать глобальное (статическое) хранилище либо для указателя на массив (динамическое выделение), либо только для фактического массива (статическое выделение).

Примечание. Ваше предположение, что стек каким-то образом «быстрее», чем куча, неверно, они оба хранятся в одной и той же оперативной памяти, вы просто обращаетесь к нему относительно разных регистров. Также я хотел бы еще раз упомянуть, насколько мне не нравится использование терминов «стек» и «куча».

person Blindy    schedule 09.02.2012
comment
Статические / глобальные переменные не обязательно должны иметь конструкторы по умолчанию, и они могут быть статически или динамически инициализированы, в зависимости от того, является ли инициализатор постоянным выражением (примерно). - person Kerrek SB; 09.02.2012
comment
Однако он говорит о массивах объектов, и у этих действительно должны быть конструкторы по умолчанию. - person Blindy; 09.02.2012
comment
Распределение в стеке определенно быстрее, чем в куче, часто на порядки. Но это не актуально для объектов, которые живут в течение всего времени выполнения программы. - person zwol; 09.02.2012
comment
Однако в своем иске он ни разу не упомянул о распределении. Вы делаете такие же предположения, как и он ... - person Blindy; 09.02.2012
comment
@Blindy: массивы могут быть инициализированы списком, что, как я считаю, также не требует конструктора по умолчанию. - person Kerrek SB; 10.02.2012
comment
@Zack - это зависит от вашей реализации кучи, но в большинстве случаев вы правы. - person BigSandwich; 10.02.2012
comment
@BigSandwich Можете указать мне контрпример? Если выделение стека происходит не так быстро, это вредит каждому вызову функции, а malloc нужно проделать довольно много работы ... - person zwol; 10.02.2012
comment
@Zack, вообще не требуется, чтобы система, на которой вы программируете, даже имела стек, который вы знаете, поэтому используемые имена неверны. Вы можете легко смоделировать то, что вы называете стеком, с помощью системы только с кучей - я сделал компилятор C для такой машины, и он отлично работал. - person Blindy; 10.02.2012
comment
@Zack, не используйте malloc :) Выделите часть памяти из ОС заранее, а затем напишите свой собственный алгоритм для ее распределения. Переопределите новое и удалите, чтобы выделить из собственной кучи. Вы можете создавать специализированные кучи для определенных типов данных. - person BigSandwich; 10.02.2012
comment
В моем конкретном случае меня беспокоит не распределение, а скорость доступа. Спасибо за ответ, очень полезно. - person Anthony; 10.02.2012

Как вы упомянули, стек часто имеет ограничения по размеру. Это оставляет вам два варианта: динамически размещать объекты или сделать их глобальными. Временные затраты на выделение всех ваших объектов один раз при запуске приложения почти наверняка не имеют большого значения. Так что просто выберите тот метод, который вам больше нравится, и сделайте это.

person Carl Norum    schedule 09.02.2012

Я думаю, вы сбиваете с толку использование стека (хранилище для локальных переменных и параметров) и данных с незаданной областью (статические переменные класса и данные, выделенные через new или malloc). Одним из подходящих решений, основанных на вашем описании, может быть статический класс, в котором ваш массив указателей объявлен как статический член класса. Это будет размещено в куче подобной структуре (возможно, даже в куче, в зависимости от вашей реализации C ++). «быстрым и грязным» решением было бы объявить массив как статическую переменную (в основном глобальную переменную), однако это не лучший подход с точки зрения ремонтопригодности.

person user1200296    schedule 09.02.2012

Лучшая концепция - «Используйте стек, когда можете, и кучу, когда нужно». Я не понимаю, почему стек не сможет вместить все ваши объекты, если они не большие или вы не работаете с системой с ограниченными ресурсами. Попробуйте использовать стек, и если он не может его обработать, время, необходимое для выделения памяти в куче, может быть выполнено на раннем этапе выполнения программы и не будет значительной проблемой.

person Foggzie    schedule 09.02.2012

Если скорость не является проблемой или вы не можете позволить себе накладные расходы, вам следует вставить объекты в std::vector. Если семантика копирования не определена для объектов, следует использовать std::vector из std::shared_ptrs.

person Arcadio Alivio Sincero    schedule 09.02.2012