Формально нет никаких ограничений на реализацию , кроме указанных в стандарте, в отношении интерфейса и сложности . Практически большинство, если не все версии , происходят из одной и той же базы кода и довольно похожи на .
Основная реализация вектора - это три указателя. Фактическая память для объектов в векторе динамически выделена. В зависимости от того, как вектор «вырос», динамическая область может содержать дополнительную память; три указателя указывают на начало начала памяти, байт после последнего байта в настоящее время используется , а байт после последнего выделенного байта. Возможно, наиболее важным аспектом реализации является то, что он разделяет выделение и инициализацию: вектор будет в во многих случаях выделять больше памяти, чем необходимо, без , строя в нем объекты и будет строить объекты при необходимости , Кроме того, когда вы удаляете объекты или очищаете вектор , он не освобождает память; он уничтожит только объекты и изменит указатель на конец используемой памяти , чтобы отразить это. Позже, когда вы вставляете объекты, вам не потребуется выделение .
Когда вы добавляете объекты за пределы выделенного пространства, вектор выделит новую большую площадь; скопируйте объекты в , затем уничтожьте объекты в старом пространстве и удалите их. Из-за ограничений сложности вектор должен расти в области экспоненциально, умножая размер на некоторую фиксированную константу (1,5 и 2 являются наиболее распространенными факторами), а не на , увеличивая ее на некоторую фиксированную сумму. В результате, если вы вырастите вектор пустым, используя push_back
, не будет слишком много перераспределений и копий; Другим результатом является то, что если вы вырастите вектор из пустого, он может в конечном итоге использовать почти в два раза больше, чем .Эти проблемы можно избежать, если вы используете с использованием std::vector<>::reserve()
.
Что касается карты, ограничения сложности и тот факт, что он должен быть заказан, означает, что необходимо использовать какое-то сбалансированное дерево. Во всех реализациях, которые я знаю, это классическое дерево красного цвета: : каждая запись выделяется отдельно, в узле , который содержит два или три указателя плюс возможно логическое значение в в дополнение к данным.
Я могу добавить, что вышеизложенное относится к оптимизированным версиям контейнеров . В обычных реализациях, когда они не оптимизированы, добавит дополнительные указатели, чтобы связать все итераторы с контейнером , чтобы они могли быть отмечены, когда в контейнере будет что-то недействительное, и чтобы они могли выполнять проверку границ .
И наконец: эти классы являются шаблонами, поэтому на практике у вас есть доступ к источникам, и вы можете посмотреть на них. (Вопросы, как безопасности исключение иногда делают реализации менее прямо вперед, чем мы могли бы, но реализация с г ++ или VC++ не так уж трудно понять.)
С классом, удерживающим векторы и карты, почти весь след указанного объекта будет динамичным (конечно же, по желанию стандартной библиотеки). Эти контейнеры будут * вероятно * иметь небольшой статический след в вашем классе объектов и значительный * динамический * след в том, как они поддерживают свой контент. – WhozCraig
@whozcraig: По динамическим следам я правильно понимаю, что вы имеете в виду память, которая не соприкасается с остальной частью моего объекта, и которая выделена и удалена во время выполнения без моего ведома? – Chap
Точно. Его все управляется стандартной библиотекой. Некоторые вещи довольно прямолинейны (например, вектор, но даже некоторые вещи могут удивить вас, когда вы рассмотрите некоторые реализации). В то время как другие могут быть довольно сложными (карты часто реализуются как RB-деревья, которые ничего, но тривиально). Я видел реализации, в которые записываются записи с одним расширением, и другие, которые используют сложные алгоритмы подкачки и размещение - «новое» управление экземплярами.Все зависит от реализации (которая должна соответствовать стандарту взаимодействия и поведения). – WhozCraig