- PVSM.RU - https://www.pvsm.ru -
После появления в стандартной библиотеке С++ умных указателей, проблема управления временем жизни объекта была решена. Можно создавать объекты на стеке, тогда они автоматичести удалятся при выходе из области видимости, либо использовать unique_ptr для создания объектов с экслюзивным владением или shared_ptr для совместного владения. Но только для shared_ptr в стандартной библиотеке существует невладеющий указатель weak_ptr, который предотвращает использование невалидного указателя. Для остальных случаев используют «старые и опасные» raw pointers.
Как же предлагают решить эту проблему разработчики языка?
На сайте CppCoreGuidelines [1] есть несколько любопытных документов (здесь [2] и здесь [3]).
Основной посыл таков: мне не можем реализовать безопасные невладеющие указатели, не нарушая zero overhead principle. Поэтому мы не будем реализовывать такие средства в runtime, а постараемся решить проблему статическим анализом кода.
Я не согласен с такой позицией и вот мои доводы:
По моему мнению, стандартная библиотека должна предоставлять классы для всех основных сценариев программирования. Невладеющие указатели и доступ по висячим ссылкам большая и до сих пор незакрытая тема в С++. Думаю стандартная библиотека должна предоставлять программисту опциональную возможность иметь такие указатели.
В своем проекте RSL [4] я попробовал реализовать такой указатель. Основная идея не нова: необходим объект, который при разрушении будет нотифицировать указатели о факте удаления.
Таким образом мы имеем два класса:
Когда rsl::track::pointer'ы указывают на один и тот же rsl::track::trackable объект, они выстраиваются в двухсвязный список. Указатель на голову списка содержится в rsl::track::trackable. Таким образом, создание указателей занимает константное время. Размер rsl::track::trackable составляет один указатель, а rsl::track::pointer — 4 указателя (указатель на объект, два указателя для организации списка и еще один для реализации полиморфного поведения). Возможно более оптимальная организация указателей, если кто знает, прошу рассказать.
Так же данная реализация не потоко безопасна, для обеспечения работы в разных потоках прийдется добавлять std::atomic_flag и замедлять модификацию указателей.
Кроме того, с появлением allocator aware containers [7], появилась возможность реализовать аллокатор [8], который позволяет использовать rsl::track::pointer со стандартными контейнерами. Основная идея в том, что теперь все аллокации в контейнерах делаются экземпляром аллокатора, хранящегося в контейнере, или его шаблонной копии, и мы можем хранить rsl::track::trackable в аллокаторе и передавать его в копии.
В тестах [9] приведены примеры работы с основными стандартными контейнерами, включая std::array, а также unique_ptr [10].
В заключении хочу привести еще один сценарий, в котором rsl::track::pointer будут полезны. Это ситуации, аналогичные delete this. Обычно такое происходит коственно при вызове врешнего по отношению к объекту кода, функтора или сигнала. Такого рода ошибки редки, но трудно уловимы.
Для таких случаев (да и любых других проблем с доступом по висячей ссылке) используют такие средства как google sanitizers [11], которые позволяют отлавливать подобные проблемы.
Но эти средства имеют свои недостатки:
Надеюсь библиотека окажется полезной С++ разработчикам. Возможно есть другие способы решения подобных проблем, с удовольствием выслушаю в комментариях.
P.S. Еще раз, для удобства, привожу ссылку на код библиотеки RSL [4].
Автор: lexxmark
Источник [12]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/programmirovanie/251665
Ссылки в тексте:
[1] CppCoreGuidelines: https://github.com/isocpp/CppCoreGuidelines
[2] здесь: https://github.com/isocpp/CppCoreGuidelines/blob/master/docs/Introduction%20to%20type%20and%20resource%20safety.pdf
[3] здесь: https://github.com/isocpp/CppCoreGuidelines/blob/master/docs/Lifetimes%20I%20and%20II%20-%20v0.9.1.pdf
[4] RSL: https://github.com/lexxmark/rsl
[5] rsl::track::trackable: https://github.com/lexxmark/rsl/blob/master/include/track/pointer.h#L13
[6] rsl::track::pointer: https://github.com/lexxmark/rsl/blob/master/include/track/pointer.h#L160
[7] allocator aware containers: http://en.cppreference.com/w/cpp/concept/AllocatorAwareContainer
[8] аллокатор: https://github.com/lexxmark/rsl/blob/master/include/track/allocator.h
[9] тестах: https://github.com/lexxmark/rsl/tree/master/tests/track
[10] unique_ptr: https://github.com/lexxmark/rsl/blob/master/tests/track/unique_ptr.cpp
[11] google sanitizers: https://github.com/google/sanitizers
[12] Источник: https://habrahabr.ru/post/325494/
Нажмите здесь для печати.