← Назад к вопросам

Сколько памяти занимает std::shared_ptr?

2.0 Middle🔥 131 комментариев
#Язык C++

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Сколько памяти занимает std::shared_ptr?

Краткий ответ

std::shared_ptr занимает два указателя памяти — это примерно 16 байт на 64-битной системе (8 байт на указатель объекта + 8 байт на указатель control block). На 32-битной системе — 8 байт.

Детальное объяснение структуры

std::shared_ptr состоит из двух компонентов:

template<typename T>
class shared_ptr {
private:
    T* ptr;                          // 8 байт (на 64-bit)
    control_block* control_block;    // 8 байт (на 64-bit)
};

Control Block — это отдельно выделенная структура в куче, которая содержит:

  • Счётчик сильных ссылок (strong references count)
  • Счётчик слабых ссылок (weak references count)
  • Данные для удаления объекта (deleter, allocator)
  • Сам объект (если создан через std::make_shared)

Разница между shared_ptr и raw pointer

// Raw pointer
int* p = new int(42);  // Занимает: 8 байт (адрес)

// std::shared_ptr
std::shared_ptr<int> sp(new int(42));  // Занимает: 16 байт

Это означает, что каждый shared_ptr имеет двойной оверхед по сравнению с обычным указателем.

Оптимизация: std::make_shared

// Неоптимально: два выделения памяти
std::shared_ptr<int> sp1(new int(42));

// Оптимально: одно выделение памяти
std::shared_ptr<int> sp2 = std::make_shared<int>(42);

Когда используешь std::make_shared:

  • Объект и control block выделяются одним блоком памяти
  • Меньше фрагментации кучи
  • Быстрее выполняется

Практический пример с вычислением

#include <iostream>
#include <memory>

int main() {
    std::cout << "Размер указателя: " << sizeof(int*) << " байт\n";
    std::cout << "Размер shared_ptr<int>: " << sizeof(std::shared_ptr<int>) << " байт\n";
    std::cout << "Размер weak_ptr<int>: " << sizeof(std::weak_ptr<int>) << " байт\n";
    
    // Вывод (на 64-bit Linux):
    // Размер указателя: 8 байт
    // Размер shared_ptr<int>: 16 байт
    // Размер weak_ptr<int>: 16 байт
    
    return 0;
}

Важно: sizeof(shared_ptr) одинаков независимо от типа объекта. Размер не зависит от того, указывает ли указатель на int или на std::string.

Расходы на synchronization

// Каждое изменение счётчика ссылок требует:
// 1. Атомарную операцию (atomic increment/decrement)
// 2. Возможно, memory barrier (если нужна synchronization)
// 3. При счётчике = 0: вызов deleter и освобождение memory

Это означает, что в многопоточном коде shared_ptr имеет накладные расходы на синхронизацию, которые замедляют работу.

Когда стоит беспокоиться?

Проблема возникает, если:

  • Собираешь массив из миллионов shared_ptr — каждый займёт 16 байт
  • У тебя критичная по памяти embedded система
  • Работаешь в real-time системах, где атомарные операции дорогие

В этом случае лучше использовать:

// 1. Обычные raw указатели (если владение ясно)
std::vector<int*> ptrs;

// 2. std::unique_ptr (8 байт, no reference counting)
std::vector<std::unique_ptr<int>> ptrs;

// 3. Пулы объектов (object pool)
// 4. Stack-based lifetime если возможно

Выводы

  • std::shared_ptr: 16 байт (два указателя на 64-bit система)
  • std::unique_ptr: 8 байт (один указатель, no overhead на control block)
  • Используй make_shared для эффективности
  • В производительностью чувствительном коде предпочитай unique_ptr или raw указатели с ясной семантикой владения