{"id":108602,"date":"2023-08-15T07:00:00","date_gmt":"2023-08-15T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=108602"},"modified":"2023-08-15T07:38:17","modified_gmt":"2023-08-15T14:38:17","slug":"20230815-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230815-00\/?p=108602","title":{"rendered":"Inside STL: The <CODE>shared_ptr<\/CODE> constructor vs <CODE>make_shared<\/CODE>"},"content":{"rendered":"<p>There are two ways to create a new object that is controlled by a <code>shared_ptr<\/code>.<\/p>\n<pre>\/\/ From a raw pointer\r\nauto p = std::shared_ptr&lt;S&gt;(new S());\r\n\r\n\/\/ Via make_shared\r\nauto p = std::make_shared&lt;S&gt;();\r\n<\/pre>\n<p>They result in two different memory layouts.<\/p>\n<p>In the first case, you manually created a new <code>S<\/code> object, and then passed a pointer to it to the <code>shared_ptr<\/code> constructor. The <code>shared_ptr<\/code> adopts the raw pointer and creates a control block to monitor its lifetime. When the last shared pointer destructs, the <code>Dispose()<\/code> method deletes the pointer you passed in.\u00b9 When the last shared or weak pointer destructs, the <code>Delete()<\/code> method deletes the control block.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse; text-align: center;\" title=\"Diagram of memory layout (see text)\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td colspan=\"2\">p<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray;\">object<\/td>\n<td style=\"border: solid 1px gray;\">\u2022<\/td>\n<td>\u2192<\/td>\n<td>\n<div style=\"border: solid 1px gray;\">S<\/div>\n<\/td>\n<td rowspan=\"2\">freed separately<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray;\">control<\/td>\n<td style=\"border: solid 1px gray;\">\u2022<\/td>\n<td>\u2192<\/td>\n<td>\n<div style=\"border: solid 1px gray;\">control_block<\/div>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>In the second case, you let the <code>make_<wbr \/>shared<\/code> function create the <code>S<\/code> object, and in practice, what it does is create a single memory allocation that consists of a control block stacked on top of an <code>S<\/code> object. This time, when the last shared pointer destructs, the <code>Dispose()<\/code> method runs the <code>S<\/code> destructor, but the memory isn&#8217;t freed yet. Only when the last shared or weak pointer destructs does the <code>Delete()<\/code> method get called to free the entire memory block.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse; text-align: center;\" title=\"Diagram of memory layout (see text)\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td colspan=\"2\">p<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray;\" rowspan=\"2\">object<\/td>\n<td style=\"border: solid 1px gray;\" rowspan=\"2\">\u2022<\/td>\n<td style=\"line-height: 50%;\">\u00a0<\/td>\n<td style=\"border: solid 1px gray;\" rowspan=\"2\">control_block<\/td>\n<td rowspan=\"4\">freed as a unit<\/td>\n<\/tr>\n<tr>\n<td rowspan=\"2\" valign=\"middle\">\u292e<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray;\" rowspan=\"2\">control<\/td>\n<td style=\"border: solid 1px gray;\" rowspan=\"2\">\u2022<\/td>\n<td style=\"border: solid 1px gray;\" rowspan=\"2\">S<\/td>\n<\/tr>\n<tr>\n<td style=\"line-height: 50%;\">\u00a0<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The two memory layouts have their own pros and cons.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>\u00a0<\/th>\n<th>Two allocations<\/th>\n<th>Single allocation<\/th>\n<\/tr>\n<tr>\n<td>Last shared<br \/>\npointer destructs<\/td>\n<td>Object destructs<br \/>\nObject memory freed<\/td>\n<td>Object destructs<br \/>\nObject memory not freed<\/td>\n<\/tr>\n<tr>\n<td>Last shared or weak<br \/>\npointer destructs<\/td>\n<td>Control block destructs<br \/>\nControl block freed<\/td>\n<td>Control block destructs<br \/>\nCombo block freed<\/td>\n<\/tr>\n<tr>\n<td>Locality<\/td>\n<td>Worse<\/td>\n<td>Better<\/td>\n<\/tr>\n<tr>\n<td>Straggler weak pointer<\/td>\n<td>Control block lingers<br \/>\n(Object memory freed already)<\/td>\n<td>Entire combo block lingers<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The single-allocation version has better memory locality since the control block is kept right next to the managed object.<\/p>\n<p>On the other hand, with the single-allocation version, a straggler weak pointer (a weak pointer which lives for a long time after the last shared pointer has destructed) prevents the entire combo memory block from being freed. By comparison, only the control block remains in memory with the two-allocation version.<\/p>\n<p>If your weak pointers exist to break circular references, then you won&#8217;t have stragglers because they will go away when the object graph destructs. Similarly, if your weak pointers are in event handlers, then those weak pointers won&#8217;t be stragglers if you are careful to unregister the event handlers at destruction. The stragglers come into play if you retain weak references in, say, a cache or other long-lifetime storage, and even then, they cause a problem only if <code>sizeof(S)<\/code> is significant or if you have a lot of them.<\/p>\n<p>Next time, we&#8217;ll look at <code>make_shared<\/code>&#8216;s close friend, <code>std::<wbr \/>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this<\/code>.<\/p>\n<p>\u00b9 More specifically, the <code>Deleter<\/code> object deletes the pointer you passed in. The default deleter uses the <code>delete<\/code> operator to delete the pointer.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Where to hide the control block.<\/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":[],"class_list":["post-108602","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing"],"acf":[],"blog_post_summary":"<p>Where to hide the control block.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108602","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=108602"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108602\/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=108602"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=108602"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=108602"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}