{"id":111526,"date":"2025-08-29T07:00:00","date_gmt":"2025-08-29T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=111526"},"modified":"2025-08-16T18:21:30","modified_gmt":"2025-08-17T01:21:30","slug":"20250829-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250829-00\/?p=111526","title":{"rendered":"Thoughts on creating a tracking pointer class, part 15: A custom shared pointer"},"content":{"rendered":"<p>Last time, we <a title=\"Thoughts on creating a tracking pointer class, part 14: Nonthrowing moves with the shared tracking pointer\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250828-00\/?p=111524\"> made our trackable object implementation&#8217;s constructors and assignment operations non-throwing<\/a>, although it came at a cost of making the act of creating a tracking pointer potentially-throwing. At the end, I noted that we didn&#8217;t use all the features of C++ shared pointers. We never used weak pointers or thread safety, so we can replace shared pointers with a custom version that supports only single-threaded shared references.<\/p>\n<p>The single-threaded simplification is significant because it removes the need for atomic operations and allows the compiler to do reordering and coalescing of reference count manipulations.<\/p>\n<pre>template&lt;typename T&gt;\r\nstruct tracking_ptr_base\r\n{\r\n    tracking_ptr_base() noexcept = default;\r\n    tracking_ptr_base(tracking_ptr_base const&amp; other) noexcept\r\n        : m_ptr(other.copy_ptr()) {}\r\n    tracking_ptr_base(tracking_ptr_base&amp;&amp; other) noexcept = default;\r\n    ~tracking_ptr_base() = default;\r\n\r\n    tracking_ptr_base&amp;\r\n        operator=(tracking_ptr_base const&amp; other) noexcept\r\n    {\r\n        m_ptr = other.copy_ptr();\r\n        return *this;\r\n    }\r\n\r\n    tracking_ptr_base&amp;\r\n        operator=(tracking_ptr_base&amp;&amp; other) noexcept = default;\r\n\r\n    operator bool() const noexcept\r\n    {\r\n        return get() != nullptr;\r\n    }\r\n\r\nprotected:\r\n    friend struct trackable_object&lt;T&gt;;\r\n\r\n    struct data\r\n    {\r\n        data(T* tracked) noexcept : m_tracked(tracked) {}\r\n        unsigned int m_refs = 1;\r\n        T* m_tracked;\r\n    };\r\n\r\n    struct deleter\r\n    {\r\n        void operator()(data* p)\r\n        {\r\n            if (--p-&gt;m_refs == 0)\r\n            {\r\n                delete p;\r\n            }\r\n        }\r\n    };\r\n\r\n    tracking_ptr_base(T* p) noexcept : m_ptr(new data(p))\r\n    {\r\n    }\r\n\r\n    T* get() const noexcept\r\n    {\r\n        return m_ptr ? m_ptr-&gt;m_tracked : nullptr;\r\n    }\r\n\r\n    void set(T* ptr) noexcept\r\n    {\r\n        m_ptr-&gt;m_tracked = ptr;\r\n    }\r\n\r\n    std::unique_ptr&lt;data, deleter&gt; copy_ptr() const noexcept\r\n    {\r\n        if (m_ptr) ++m_ptr-&gt;m_refs;\r\n        return std::unique_ptr&lt;data, deleter&gt;(m_ptr.get());\r\n    }\r\n\r\n    std::unique_ptr&lt;data, deleter&gt; m_ptr;\r\n};\r\n<\/pre>\n<p>This looks like a lot of code, but it&#8217;s really accomplishing very little.<\/p>\n<p>The basic operations on the pointer are incref and decref. The incref operation increments the <code>m_refs<\/code> and the decref operation decrements the <code>m_refs<\/code>, destroying the <code>data<\/code> if the reference count goes to zero.<\/p>\n<p>Copying the <code>tracking_ptr_base<\/code> copies the pointer and performs an incref. Destructing the <code>tracking_ptr_base<\/code> performs a decref, and moving the <code>tracking_ptr_base<\/code> moves the pointer from the source to the destination, decref&#8217;ing any existing pointer in the destination. (The responsibility to decref the pointer moves from the source to the destination.)<\/p>\n<p>By building on top of <code>std::<wbr \/>unique_ptr<\/code> with a custom deleter, we get cleanup and move implementations for free.<\/p>\n<p>Okay, I think this mostly wraps up the development of a tracking pointer implementation. I have no idea if it is any use, but it was a nice exercise trying to figure out how to implement it.<\/p>\n<p>(Okay, there&#8217;s a follow-up I had promised to write after the main series is over. So there&#8217;s at least one more part to go.)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Simplifying it for out limited use case.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-111526","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Simplifying it for out limited use case.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111526","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=111526"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111526\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=111526"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=111526"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=111526"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}