{"id":15953,"date":"2009-11-20T07:00:00","date_gmt":"2009-11-20T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2009\/11\/20\/the-difference-between-assignment-and-attachment-with-atl-smart-pointers\/"},"modified":"2009-11-20T07:00:00","modified_gmt":"2009-11-20T07:00:00","slug":"the-difference-between-assignment-and-attachment-with-atl-smart-pointers","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20091120-00\/?p=15953","title":{"rendered":"The difference between assignment and attachment with ATL smart pointers"},"content":{"rendered":"<p>\nLast time,\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2009\/11\/19\/9924950.aspx\">\nI presented a puzzle regarding a memory leak<\/a>.\nHere&#8217;s the relevant code fragment:\n<\/p>\n<pre>\n<font COLOR=\"red\">CComPtr&lt;IStream&gt; pMemoryStream;<\/font>\nCComPtr&lt;IXmlReader&gt; pReader;\nUINT nDepth = 0;\n\/\/Open read-only input stream\n<font COLOR=\"red\">pMemoryStream = ::SHCreateMemStream(utf8Xml, cbUtf8Xml);<\/font>\n<\/pre>\n<p>\nThe problem here is assigning the return value of\n<code>SHCreateMemStream<\/code> to a smart pointer\ninstead of attaching it.\n<\/p>\n<p>\nThe <code>SHCreateMemStream<\/code> function creates a memory stream\nand returns a pointer to it.\nThat pointer has a reference count of one,\nin accordance with COM rules that a function\n<a HRef=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms692481.aspx\">\nwhich produces a reference calls <code>AddRef<\/code>,\nand the responsibility is placed upon the recipient\nto call <code>Release<\/code><\/a>.\nThe assignment operator for <code>CComPtr&lt;T&gt;<\/code> is a copy operation:\nIt <code>AddRef<\/code>s the pointer and saves it.\nYou&#8217;re still on the hook for the reference count of the original pointer.\n<\/p>\n<pre>\nATLINLINE ATLAPI_(IUnknown*) AtlComPtrAssign(IUnknown** pp, IUnknown* lp)\n{\n        if (lp != NULL)\n                lp-&gt;AddRef();\n        if (*pp)\n                (*pp)-&gt;Release();\n        *pp = lp;\n        return lp;\n}\ntemplate &lt;class T&gt;\nclass CComPtr\n{\npublic:\n        ...\n        T* operator=(T* lp)\n        {\n                return (T*)AtlComPtrAssign((IUnknown**)&amp;p, lp);\n        }\n<\/pre>\n<p>\nObserve that assigning a <code>T*<\/code> to a <code>CComPtr&lt;T&gt;<\/code>\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2004\/04\/06\/108395.aspx\">\n<code>AddRef<\/code>s the incoming pointer<\/a>\nand\n<code>Release<\/code>s the old pointer (if any).\nWhen the <code>CComPtr&lt;T&gt;<\/code> is destructed, it will release\nthe pointer, undoing the <code>AddRef<\/code> that was performed by\nthe assignment operator.\nIn other words, assignment followed by destruction has a net effect\nof zero on the pointer you assigned.\nThe operation behaves like a copy.\n<\/p>\n<p>\nAnother way of putting a pointer into a <code>CComPtr&lt;T&gt;<\/code>\nis with the <code>Attach<\/code> operator.\nThis is a transfer operation:\n<\/p>\n<pre>\n        void Attach(T* p2)\n        {\n                if (p)\n                        p-&gt;Release();\n                p = p2;\n        }\n<\/pre>\n<p>\nObserve that there is no <code>AddRef<\/code> here.\nWhen the <code>CComPtr&lt;T&gt;<\/code> is destructed,\nit will perform the <code>Release<\/code>,\nwhich doesn&#8217;t undo any operation performed by the <code>Attach<\/code>.\nInstead, it releases the reference count held by the original pointer\nyou attached.\n<\/p>\n<p>\nLet&#8217;s put this in a table, since people seem to like tables:\n<\/p>\n<table BORDER=\"1\" STYLE=\"border-collapse: collapse\" CELLPADDING=\"3\">\n<tr>\n<th>Operation<\/th>\n<th>Behavior<\/th>\n<th>Semantics<\/th>\n<\/tr>\n<tr>\n<td>Attach()<\/td>\n<td>Takes ownership<\/td>\n<td>Transfer semantics<\/td>\n<\/tr>\n<tr>\n<td>operator=()<\/td>\n<td>Creates a new reference<\/td>\n<td>Copy semantics<\/td>\n<\/tr>\n<\/table>\n<p>\nYou use the <code>Attach<\/code> method when you want to assume\nresponsibility for releasing the pointer (ownership transfer).\nYou use the assignment operator when you want the original pointer\nto continue to be responsible for its own release (no ownership transfer).\n<\/p>\n<p>\nThere is also a <code>Detach<\/code> method which is the opposite of\n<code>Attach<\/code>:\nDetaching a pointer from the <code>CComPtr&lt;T&gt;<\/code>\nmeans &#8220;I am taking over responsibility for releasing this pointer.&#8221;\nThe <code>CComPtr&lt;T&gt;<\/code> gives you its pointer and then forgets\nabout it; you&#8217;re now on your own.\n<\/p>\n<p>\nThe memory leak in the code fragment above occurs because the\nassignment operator has copy semantics, but we wanted transfer\nsemantics,\nsince we want the smart pointer to take the responsibility for\nreleasing the pointer when it is destructed.\n<\/p>\n<pre>\npMemoryStream.Attach(::SHCreateMemStream(utf8Xml, cbUtf8Xml));\n<\/pre>\n<p>\nThe <code>CComPtr&lt;T&gt;::operator=(T*)<\/code> method\nis definitely one of the more dangerous methods in the\n<code>CComPtr&lt;T&gt;<\/code> repertoire,\nbecause it&#8217;s so easy to assign a pointer to a smart pointer\nwithout giving it a moment&#8217;s thought.\n(Another dangerous method is the\n<code>T** CComPtr&lt;T&gt;::operator&amp;()<\/code>,\nbut at least that has an assertion to try to catch the bad usages.\nEven nastier is\n<a HREF=\"http:\/\/blogs.msdn.com\/jaredpar\/archive\/2009\/11\/04\/type-safety-issue-when-assigning-ccomptr-t-instances.aspx\">\nthe secret QI&#8217;ing assignment operator<\/a>.)\nI have to say that there is merit to\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2004\/04\/06\/108395.aspx#108407\">\nBen Hutchings&#8217; recommendation<\/a> simply not to allow a simple pointer\nto be assigned to a smart pointer, precisely because the semantics are\neasily misunderstood.\n(The boost library, for example, follows Ben&#8217;s recommendation.)\n<\/p>\n<p>\nHere&#8217;s another exercise based on what you&#8217;ve learned:\n<\/p>\n<blockquote CLASS=\"q\">\n<p>\nApplication Verifier told us that we have a memory leak,\nand we traced it back to the\nfunction <code>GetTextAsInteger<\/code>.\n<\/p>\n<pre>\nBSTR GetInnerText(IXMLDOMNode *node)\n{\n    BSTR bstrText = NULL;\n    node-&gt;get_text(&amp;bstrText);\n    return bstrText;\n}\nDWORD GetTextAsInteger(IXMLDOMNode *node)\n{\n    DWORD value = 0;\n    CComVariant innerText = GetInnerText(node);\n    hr = VariantChangeType(&amp;innerText, &amp;innerText, 0, VT_UI4);\n    if (SUCCEEDED(hr))\n    {\n        value = V_UI4(&amp;innerText);\n    }\n    return value;\n}\n<\/pre>\n<p>\nObviously, the problem is that we passed the same input and output\npointers to <code>VariantChangeType<\/code>,\ncausing the output integer to overwrite the input <code>BSTR<\/code>,\nresulting in the leak of the <code>BSTR<\/code>.\nBut when we fixed the function, we still got the leak:\n<\/p>\n<pre>\nDWORD GetTextAsInteger(IXMLDOMNode *node)\n{\n    DWORD value = 0;\n    CComVariant innerText = GetInnerText(node);\n    <font COLOR=\"blue\">CComVariant textAsValue;<\/font>\n    hr = VariantChangeType(&amp;innerText, &amp;<font COLOR=\"blue\">textAsValue<\/font>, 0, VT_UI4);\n    if (SUCCEEDED(hr))\n    {\n        value = V_UI4(&amp;<font COLOR=\"blue\">textAsValue<\/font>);\n    }\n    return value;\n}\n<\/pre>\n<p>\nIs there a leak in the <code>VariantChangeType<\/code> function itself?\n<\/p>\n<\/blockquote>\n<p>\nHint: It is in fact explicitly documented that the output parameter\nto <code>VariantChangeType<\/code> can be equal to the input parameter,\nwhich results in an in-place conversion.\nThere was nothing wrong with the original call to\n<code>VariantChangeType<\/code>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Last time, I presented a puzzle regarding a memory leak. Here&#8217;s the relevant code fragment: CComPtr&lt;IStream&gt; pMemoryStream; CComPtr&lt;IXmlReader&gt; pReader; UINT nDepth = 0; \/\/Open read-only input stream pMemoryStream = ::SHCreateMemStream(utf8Xml, cbUtf8Xml); The problem here is assigning the return value of SHCreateMemStream to a smart pointer instead of attaching it. The SHCreateMemStream function creates a memory [&hellip;]<\/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-15953","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Last time, I presented a puzzle regarding a memory leak. Here&#8217;s the relevant code fragment: CComPtr&lt;IStream&gt; pMemoryStream; CComPtr&lt;IXmlReader&gt; pReader; UINT nDepth = 0; \/\/Open read-only input stream pMemoryStream = ::SHCreateMemStream(utf8Xml, cbUtf8Xml); The problem here is assigning the return value of SHCreateMemStream to a smart pointer instead of attaching it. The SHCreateMemStream function creates a memory [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/15953","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=15953"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/15953\/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=15953"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=15953"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=15953"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}