Поэтому я подумываю о том, чтобы сделать глобальную вложенную std::vector
(vector <vector <data>> V
) изначально пустой, а позже в ходе программы добавлять данные в эту вложенную vector
. Меня беспокоит добавление слишком большого количества элементов во время выполнения, превышение стека и его переполнение.
Итак, после некоторых исследований я понял, что внешний экземпляр vector
V
— это объект в стеке, но его содержимое, такое как внутренний vector
и элементы внутри внутренних vector
, хранятся в куче.
Означает ли это, что мне не нужно беспокоиться о добавлении слишком большого количества элементов во вложенный вектор во время выполнения, поскольку элементы будут храниться в куче, а не в стеке?
Ниже у меня есть изображение того, как, по моему мнению, будет выглядеть вложенный вектор V
в памяти. Скажите, пожалуйста, верно ли мое нынешнее понимание. Спасибо
Мой прогноз вложенной векторной памяти:
Попытка понять структуру вложенной векторной памяти и добавление слишком большого количества элементов приведет к переполнению стека.
... и насколько я вижу, ваш прогноз выглядит верным в тех реализациях, которые я рассмотрел.
Дополнительное чтение:devblogs.microsoft.com/oldnewthing/20230802-00/?p=108524
Примечание: куча и стек — это детали реализации, а стандарт C++ описывает необходимое поведение с точки зрения динамического и автоматического хранения. Вероятно, вы никогда не увидите такого в реальной жизни, но пока соблюдаются требования, предъявляемые к динамическому и автоматическому хранению, можно использовать что угодно. Сюда входят Стоящие камни, измельченный в порошок рог единорога, пыльца пикси и начинка Заботливого медведя, пропитанная кровью ритуально убитых смурфиков. Проклятие. Внезапно стало темно.
не совсем то же самое, но в основном: stackoverflow.com/questions/55478523/…
Примечание: Дуг приводит очень простой пример того, как избежать вложенного вектора без необходимости изменения синтаксиса. Зачем вам это делать? Все эти различные блоки памяти действительно могут снизить производительность программы, поскольку процессору приходится преследовать все эти указатели и почти наверняка отдельно загружать данные, на которые ссылаются указатели. Обычно лучше хранить все данные в одном компактном блоке.
sizeof(V)
сообщит вам, сколько байтов V
занимает в стеке.
Если ответ решает вашу проблему @Sliferslacker, вы можете нажать «✔», чтобы отметить его как принятый ответ. Вы также можете проголосовать за все полезные ответы (голосование и принятие выполняются отдельно). Смотрите здесь: Что мне делать, если кто-то отвечает на мой вопрос?.
std::vector
ни для чего не использует стек; все в векторе находится в куче.
Конечно, на всякий случай: если положить вектор в стек, как в
void foo()
{
std::vector<...> something;
}
тогда фактическое содержимое будет находиться в куче, как я только что сказал, но будет небольшой кусочек управляющих данных, которые составляют фактический экземпляр "вектора" в стеке - например, "указатель на блок памяти, размер блока памяти, и количество используемых в настоящее время элементов».
Если вы сделаете это с вложенным вектором, как в
void foo()
{
std::vector<std::vector<...>> something;
}
Тогда «данные управления внешним вектором» будут находиться в стеке, а все данные управления внутренним вектором, которые являются элементами содержимого внешнего вектора, будут находиться в куче.
если хочешь, попробуй что-нибудь вроде этого:
#include <vector>
#include <iostream>
int main()
{
std::vector<int> test;
std::cout << sizeof(test) << '\n';
for (int i=0; i<10000; i++) test.push_back(i);
std::cout << sizeof(test) << '\n';
}
Вы должны получить довольно небольшое число размера векторного объекта, и независимо от того, насколько сильно вы в него вдавливаете, это не изменится. И да, любой «эксперт» по C++ знает, что sizeof()
объекта не может измениться, но новичку, возможно, захочется увидеть, чтобы он не менялся :-)
Спасибо, подтвердили. Верно ли это и для других контейнеров с динамическим размером, таких как очереди, стек, деки, приоритетные очереди и т. д. Где содержимое элементов находится в куче, а не в стеке?
На самом деле это не предусмотрено стандартом, но на практике std::vector
реализации всегда выделяют память для элементов в куче.
Следовательно, ваше понимание верно, и вам не нужно беспокоиться о переполнении стека при использовании std::vector
.
Сам объект std::vector
очень легкий и содержит лишь несколько элементов типа «заголовка» (один из них указывает на фактические данные в куче, другие могут указывать на размер, емкость и т. д. — это зависит от реализации).
В случае вложенного vector
в стеке может находиться только «заголовок» внешнего элемента (если вы, например, используете его как локальную переменную).
Другие ответы здесь хороши и предоставляют правильную информацию. Но для наглядности вот красивая картинка:
Это общий случай с объектами-контейнерами C++. Сам объект вы создаете в стеке (как локальную переменную), но его данные выделяются и сохраняются в куче. В случае вложенных объектов, таких как вложенный вектор, все, кроме самого внешнего объекта, снова выделяется в куче.
Фактически, объекты-контейнеры C++ имеют класс Allocator как часть своего шаблона, целью которого является управление данными объекта (которые по умолчанию находятся в куче).
std::vector
гарантирует непрерывное расположение своих данных. Но вы видите, что он не объединяет вещи, как это делает многомерный массив. Содержащиеся объекты представляют собой векторы, которые действительно хранятся как непрерывные данные, но, как вы можете видеть, каждый векторный объект поддерживает свое собственное отдельное хранилище данных в куче.
Ваше понимание правильное — вам не нужно беспокоиться о пространстве стека при использовании
std::vector
, поскольку память для элементов выделяется в куче. Наличие вложенного вектора не меняет этого. В стеке может находиться только легкий «заголовок» внешнего вектора (если это, например, локальная переменная).