Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Сколько памяти занимает 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 указатели с ясной семантикой владения