Сколько требуется памяти для хранения std::map?

Ответ

Память для std::map зависит от реализации (обычно красно-черное дерево), размера ключа и значения, а также от аллокатора. На 64-битной системе каждый узел содержит:

  • Пару ключ-значение: sizeof(std::pair<const Key, Value>)
  • Три указателя (левый, правый, родительский): 3 * 8 байт = 24 байта
  • Цвет узла (обычно bool или char): 1 байт
  • Выравнивание (padding) — зависит от платформы

Пример оценки для std::map<int, std::string>:

#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<int, std::string> m;
    m[1] = "test";

    // Оценка размера одного узла:
    // - int: 4 байта
    // - std::string (small string optimization): обычно 24-32 байта
    // - Указатели: 24 байта
    // - Цвет: 1 байт
    // Итого: ~53-61 байт + выравнивание

    std::cout << "Размер пустого map: " << sizeof(m) << " байтn";
    // На практике память растёт линейно O(n), но с существенными накладными расходами
    // по сравнению с vector из-за структуры дерева.
    return 0;
}

Для точных измерений в реальных проектах я использую специализированные инструменты вроде valgrind --tool=massif или кастомные аллокаторы с подсчётом.

Ответ 18+ 🔞

Давай разберем эту хрень по полочкам, а то народ путается, как будто в тёмной комнате хуй с горы ищет.

Смотри, std::map — это не какой-нибудь vector, где всё подряд лежит. Это дерево, ёпта, обычно красно-чёрное. И память оно жрёт, как не в себя, потому что каждый узел — это целая отдельная песочница.

На нормальной 64-битной системе в каждом узле сидит:

  • Сама парочка ключ-значение: sizeof(std::pair<const Key, Value>). Это основа.
  • Три указателя: на левого ребёнка, правого и на родителя. Это уже 3 * 8 = 24 байта, ядрёна вошь!
  • Ещё цвет узла (чтобы дерево было красивое, красно-чёрное) — обычно bool или char, ещё байт.
  • И куча выравнивания (padding), потому что процессор любит, когда всё аккуратно лежит. Без этого никуда.

Вот смотри, накидал примерчик для std::map<int, std::string>:

#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<int, std::string> m;
    m[1] = "test";

    // Оценка размера одного узла:
    // - int: 4 байта
    // - std::string (small string optimization): обычно 24-32 байта
    // - Указатели: 24 байта
    // - Цвет: 1 байт
    // Итого: ~53-61 байт + выравнивание

    std::cout << "Размер пустого map: " << sizeof(m) << " байтn";
    // На практике память растёт линейно O(n), но с существенными накладными расходами
    // по сравнению с vector из-за структуры дерева.
    return 0;
}

Видишь, какая овердохуища накладных расходов? Почти как у чиновника на госзакупках. Если тебе просто тупо хранить кучу пар, то vector<std::pair<>> будет в разы экономнее, потому что там нет этих ебушки-воробушки указателей на каждого родственника.

А если тебе реально надо знать, сколько именно памяти сожрала твоя мапа в бою, то sizeof — это просто смех. Он размер самого объекта покажет, а не всей кучи памяти, которую оно на аллокациях отгрызло. Для этого уже нужны тяжёлые инструменты: valgrind с --tool=massif или свой аллокатор-счётчик, который каждый чих записывает. Без этого всё — гадание на кофейной гуще, доверия ебать ноль.