Как устроен счетчик ссылок shared_ptr?

Я довольно новичок в C++. Я думал, что shared_ptr хранит отдельный указатель на отдельную переменную счетчика ссылок. Но сегодня я вдруг понял, что на самом деле я не знаю, как это устроено, и ссылочное значение не обязательно должно иметь отдельный указатель в C++.

Если макет определен стандартом, каков правильный ожидаемый макет std::shared_ptr?


person eonil    schedule 13.07.2013    source источник
comment
Стандарт не определяет макет (например, обязательные элементы данных) для большинства классов, std::shared_ptr является одним из них. Это требует только определенного поведения, которое включает в себя существование некоторых функций-членов и функций, не являющихся членами.   -  person dyp    schedule 13.07.2013
comment
@DyP Можете ли вы переписать это как ответ? Так что я могу выбрать его.   -  person eonil    schedule 13.07.2013
comment
В дополнение к принятому и правильному ответу, вот несколько изображений ascii, которые показывают типичный макет shared_ptr: stackoverflow.com/questions/8645835/   -  person Howard Hinnant    schedule 14.07.2013
comment
@HowardHinnant Это именно то, что я искал. Спасибо.   -  person eonil    schedule 14.07.2013


Ответы (1)


Стандарт С++ не определяет для любого класса нестандартного макета, как он должен быть размещен в памяти, например. [класс.память]/13

Нестатические элементы данных класса (не объединенного) с одинаковым контролем доступа (раздел 11) распределяются таким образом, чтобы более поздние члены имели более высокие адреса в объекте класса. Порядок размещения нестатических элементов данных с различным контролем доступа не определен (раздел 11). Требования выравнивания реализации могут привести к тому, что два соседних элемента не будут выделены сразу друг за другом; так же как и требования к пространству для управления виртуальными функциями (10.3) и виртуальными базовыми классами (10.1).

Есть некоторые исключения/упрощения для стандартных типов компоновки, но нет общей спецификации для компоновки памяти классов.

Это также относится к классам в стандартной библиотеке. Кроме того, Стандарт определяет только требования к этим классам, некоторые из них являются «интерфейсами» в смысле сигнатур функций-членов. Очень явно в [objects.within.classes]:

1) Пункты с 18 по 30 и Приложение D [т.е. Стандартная библиотека] не определяют представление классов и намеренно опускают спецификацию членов класса (9.2). Реализация может определять статические или нестатические члены класса или и то, и другое, если это необходимо для реализации семантики функций-членов, указанных в пунктах 18–30 и приложении D.

2) Объекты определенных классов иногда требуют внешних спецификаций своих классов для хранения данных, по-видимому, в объектах-членах. Для наглядности в некоторых подпунктах представлены репрезентативные объявления и семантические требования для частных объектов-членов классов, которые соответствуют внешним спецификациям классов. За объявлениями таких объектов-членов и определениями связанных типов членов следует комментарий, который заканчивается только описанием, например:

streambuf* sb; // exposition only

Тем не менее, несколько замечаний по необходимой функциональности для std::shared_ptr:

  • вам нужно сохранить счетчик ссылок для принадлежащего объекта в динамически выделяемом объекте, который содержит эту информацию о владении (dyn. alloc. потому что неясно, какой shared_ptr является последним живым, и этот последний должен освободить его)
  • вам также необходимо сохранить счетчик ссылок этого информационного объекта владельца для использования weak_ptr, так как weak_ptr::expired и weak_ptr::lock и т. д. не могут отказаться (например, из-за нарушения прав доступа)

Педантичное примечание: Стандарт не требует shared_ptr не утечки памяти, но типичная реализация для архитектуры типа ПК, вероятно, будет использовать динамическое выделение памяти.

Кстати, std::make_shared считается более быстрым, чем использование ctor std::shared_ptr, потому что он может выделять память как для принадлежащего объекта, так и для объекта информации о владении за одно выделение (в стандарте говорится, что «реализации должны выполнять не более одного выделения памяти», хотя это всего лишь Примечание).

person dyp    schedule 13.07.2013