Почему реализации интеллектуальных указателей C++ хранят счетчик ссылок в куче вместе с pointee?
Читая Alexandrescu и wikipipidia, я вижу, что pointee и счетчик ссылок хранятся в куче. Тогда есть упоминание, что подсчет ссылок неэффективен, так как счетчик должен быть размещен в куче? Почему он не хранится в стеке?
4 ответа
Потому что вы потеряете его, как только текущий экземпляр умного указателя выйдет из области видимости.
Интеллектуальный указатель используется для имитации объектов автоматического хранения, которые были выделены динамически. Сами умные указатели управляются автоматически. Поэтому, когда человек уничтожается, все, что он хранит в автоматическом хранилище, также уничтожается. Но вы не хотите терять счетчик ссылок. Таким образом, вы храните его в динамическом хранилище.
Он не может быть сохранен в стеке, потому что тогда копия объекта также приведет к копии refcount, что нарушит его назначение.
Существуют разные типы умных указателей, предназначенных для разных целей. Указатель, о котором вы говорите, является общим интеллектуальным указателем (std::shared_ptr
), который помогает разделить владение объектом из нескольких мест. Все копии shared_ptr
увеличивать и уменьшать одну и ту же переменную-счетчик, которая помещается в кучу, так как она должна быть доступна для всех копий shared_ptr
даже после того, как первая копия умирает.
Так, shared_ptr
внутренне хранит два указателя: на объект и на прилавок. псевдокод:
class SharedPointer<T> {
public:
// ...
private:
T* obj;
int* counter;
}
Кстати, когда вы создаете объект с std::make_shared
реализация может оптимизировать распределения, выделяя достаточное количество памяти для хранения счетчика и объекта, а затем создавая их рядом.
Этот трюк в крайности дает нам навязчивую схему подсчета ссылок: объект внутренне удерживает свой счетчик и обеспечивает AddRef
а также Release
функции для увеличения и уменьшения. Вы можете использовать навязчивый умный указатель, например, boost::intrusive_ptr
, который использует этот механизм и, следовательно, не нужно выделять другой отдельный счетчик. Это быстрее с точки зрения распределений, но требует внедрения счетчика в определение управляемого класса.
Кроме того, когда вам не нужно совместно использовать владение объектом и нужно только контролировать его время жизни (то есть, которое разрушается при возврате функции), вы можете использовать умный указатель с областью видимости: std::unique_ptr
или же boost::scoped_ptr
, Счетчик вообще не нужен, так как только одна копия unique_ptr
существует.
Как уже отмечали другие, стек не является подходящим местом для хранения счетчика ссылок, потому что объект может пережить текущий кадр стека (в этом случае счетчик ссылок исчезнет!)
Стоит отметить, что некоторые недостатки, связанные с размещением счетчика ссылок в куче, можно преодолеть, храня его "вместе" с самим объектом. В boost это можно сделать с помощью boost:: make_shared (для shared_ptr) или boost:: intrusive_ptr.