<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>kafka0102的边城客栈 &#187; 智能指针</title>
	<atom:link href="http://www.kafka0102.com/tag/%e6%99%ba%e8%83%bd%e6%8c%87%e9%92%88/feed" rel="self" type="application/rss+xml" />
	<link>http://www.kafka0102.com</link>
	<description>要有最朴素的生活与最遥远的梦想，即使明日天寒地冻、路远马亡。</description>
	<lastBuildDate>Sat, 18 Jun 2011 04:20:42 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>C++智能指针之auto_ptr实现分析</title>
		<link>http://www.kafka0102.com/2010/02/62.html</link>
		<comments>http://www.kafka0102.com/2010/02/62.html#comments</comments>
		<pubDate>Wed, 24 Feb 2010 18:36:34 +0000</pubDate>
		<dc:creator>kafka0102</dc:creator>
				<category><![CDATA[c++]]></category>
		<category><![CDATA[auto_ptr]]></category>
		<category><![CDATA[智能指针]]></category>
		<category><![CDATA[源码分析]]></category>

		<guid isPermaLink="false">http://www.kafka0102.com/?p=62</guid>
		<description><![CDATA[C++的动态内存的分配与释放是个挺折磨人的事情，尤其当业务逻辑变得复杂时（比如一堆try catch中，各catch里需要做delete 掉相关的堆上分配的内存），极有可能产生内存泄露的情况，尤其是一些很难触发的分支条件。C++中提供了智能指针作为可选的解决方案， C++标准库中自带的智能指针是auto_ptr，它在大多数场景下是满足需求的。针对auto_ptr的缺点，boost和loki两套库都扩展出一些智能指针，并且boost中有两位幸运儿入选了tr1中（std::tr1::shared_ptr,std::tr1::weak_ptr）。本文就gcc中auto_ptr的实现做些分析，以飨自己。笔记采用注释源码的方式。]]></description>
			<content:encoded><![CDATA[<p><span style="font-size: medium;">C++的动态内存的分配与释放是个挺折磨人的事情，尤其异常分支复杂时（比如一堆try catch中，各catch里需要做delete 掉相关的堆上分配的内存），极有可能产生内存泄露的情况。C++中提供了智能指针作为可选的解决方案， C++标准库中自带的智能指针是auto_ptr，它在大多数场景下是满足需求的。针对auto_ptr的缺点，boost和loki两套库都扩展出一些智能指针，并且boost中有两位幸运儿入选了tr1中（std::tr1::shared_ptr,std::tr1::weak_ptr）。本文就gcc中auto_ptr的实现做些分析，以飨自己。笔记采用注释源码的方式。</span></p>
<p>/**<br />
*  这个wrapper类提供auto_ptr以引用语义，在下面的操作中有介绍。<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">template<span style="color: #339933;">&lt;</span>typename _Tp1<span style="color: #339933;">&gt;</span>
<span style="color: #993333;">struct</span> auto_ptr_ref <span style="color: #009900;">&#123;</span>
	_Tp1<span style="color: #339933;">*</span> _M_ptr<span style="color: #339933;">;</span>
	explicit auto_ptr_ref<span style="color: #009900;">&#40;</span>_Tp1<span style="color: #339933;">*</span> __p<span style="color: #009900;">&#41;</span> <span style="color: #339933;">:</span>
		_M_ptr<span style="color: #009900;">&#40;</span>__p<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<div id="_mcePaste">/**</div>
<div id="_mcePaste">* auto_ptr的实现还是很简单的，使用上也简单。在创建auto_ptr对象后，</div>
<div id="_mcePaste">* 通常的使用也就是调用它的*和-&gt;操作符，如下面的sample片段：</div>
<div id="_mcePaste">* AutoPtr&lt;Admin&gt; ptr1(new Admin());</div>
<div id="_mcePaste">* cout&lt;&lt;ptr1-&gt;getAge()&lt;&lt;endl;</div>
<div id="_mcePaste">* cout&lt;&lt;&#8221;obj:&#8221;&lt;&lt;*ptr1&lt;&lt;endl;</div>
<div id="_mcePaste">* 可以看到，auto_ptr中的成员函数都是throw()不抛异常的。</div>
<div id="_mcePaste">*/</div>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">template<span style="color: #339933;">&lt;</span>typename _Tp<span style="color: #339933;">&gt;</span>
class auto_ptr <span style="color: #009900;">&#123;</span>
private<span style="color: #339933;">:</span>
	_Tp<span style="color: #339933;">*</span> _M_ptr<span style="color: #339933;">;</span><span style="color: #666666; font-style: italic;">//</span>
&nbsp;
public<span style="color: #339933;">:</span>
	<span style="color: #666666; font-style: italic;">/// The pointed-to type.</span>
	<span style="color: #993333;">typedef</span> _Tp element_type<span style="color: #339933;">;</span></pre></div></div>

<p>/**<br />
*  构造函数，将auto_ptr绑定到指针__p。<br />
*  __p是一个指向new出来的对象的指针，默认为0（NULL）是说auto_ptr的构造函数可以不传参构造，<br />
*  这时成员_M_ptr=0，如果接着解引用auto_ptr对象，将Segmentation fault。当然，通常应用auto_ptr的构造<br />
*  函数会传参的。auto_ptr提供了get函数来判断_M_ptr是否为空、reset函数重置_M_ptr指针。<br />
*  在继承情况下，_M_ptr可以是__p的基类型。<br />
*  构造函数声明为explicit表示禁止参数的自动类型转换（因为它们总是邪恶的）。<br />
*<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">	explicit auto_ptr<span style="color: #009900;">&#40;</span>element_type<span style="color: #339933;">*</span> __p <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> throw <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">:</span>
		_M_ptr<span style="color: #009900;">&#40;</span>__p<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p>/**<br />
* 辅助函数<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">	element_type<span style="color: #339933;">*</span>	release<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> throw <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		element_type<span style="color: #339933;">*</span> __tmp <span style="color: #339933;">=</span> _M_ptr<span style="color: #339933;">;</span>
		_M_ptr <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
		<span style="color: #b1b100;">return</span> __tmp<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p>/**<br />
* auto_ptr的复制构造函数是很邪恶的，它的逻辑是将参数__a中的指针挪给新对象的，<br />
* 原来的__a的内置指针被置空，接下来就不能继续操作__a引用的对象，否则就掉进出错的陷阱。<br />
* 另外，复制构造函数的参数不是个const，因为它需要修改参数内容的。<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">			auto_ptr<span style="color: #009900;">&#40;</span>auto_ptr<span style="color: #339933;">&amp;</span> __a<span style="color: #009900;">&#41;</span> throw <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">:</span>
		_M_ptr<span style="color: #009900;">&#40;</span>__a.<span style="color: #202020;">release</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p>/**<br />
* 成员函数模板。好吧，我承认，我对模板也是半知半解（注意，不是一知半解）。这个函数<br />
* 用于将继承体系中的子类型上溯成基类型。举个例子：<br />
* 假如User是基类，Admin是派生类，那么下面的操作是ok的。<br />
* auto_ptr&lt;Admin&gt; ptr2(new Admin());<br />
* auto_ptr&lt;User&gt; ptr3(ptr2);<br />
* 编译器的原理类型识别和转换的大致过程是：当模板参数类型不匹配时（_Tp1转成_Tp），<br />
* 编译首先检查是否存在合适的类型转换操作符（auto_ptr是没有的），如果没有则检查是否<br />
* 存在合适的成员函数模板完成类型转换。在_Tp1是_Tp的派生类的情况下，这种转换就会成功。<br />
* 这也是说，编译器检查的是模板参数类型而不是对象的实际类型，所以，下面的例子编译就会失败：<br />
* auto_ptr&lt;User&gt; ptr2(new Admin());<br />
* auto_ptr&lt;Admin&gt; ptr3(ptr2);<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">	template<span style="color: #339933;">&lt;</span>typename _Tp1<span style="color: #339933;">&gt;</span>	auto_ptr<span style="color: #009900;">&#40;</span>auto_ptr<span style="color: #339933;">&lt;</span>_Tp1<span style="color: #339933;">&gt;&amp;</span> __a<span style="color: #009900;">&#41;</span> throw <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">:</span>
		_M_ptr<span style="color: #009900;">&#40;</span>__a.<span style="color: #202020;">release</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p>/**<br />
*  @brief 重置管理的对象指针，如果_M_ptr不为空，会delete掉。如果重置的指针就是本身<br />
*  的_M_ptr，就是个空操作。<br />
*  @param  __p 对象指针.<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">	<span style="color: #993333;">void</span> reset<span style="color: #009900;">&#40;</span>element_type<span style="color: #339933;">*</span> __p <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> throw <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>__p <span style="color: #339933;">!=</span> _M_ptr<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			delete _M_ptr<span style="color: #339933;">;</span>
			_M_ptr <span style="color: #339933;">=</span> __p<span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p>/**<br />
* 赋值操作符，和复制构造函数一样是邪恶的，赋值操作会delete掉右值管理的对象指针。<br />
* 如果auto_ptr对象作为函数参数传递，并且是传值，那么这个调用过程会涉及到赋值操作符<br />
* 的调用，产生并不期待的delete外部对象的结果。<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">		auto_ptr<span style="color: #339933;">&amp;</span> operator<span style="color: #339933;">=</span><span style="color: #009900;">&#40;</span>auto_ptr<span style="color: #339933;">&amp;</span> __a<span style="color: #009900;">&#41;</span> throw <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		reset<span style="color: #009900;">&#40;</span>__a.<span style="color: #202020;">release</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #b1b100;">return</span> <span style="color: #339933;">*</span>this<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p>/**<br />
* 成员函数模板赋值操作符<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">	template<span style="color: #339933;">&lt;</span>typename _Tp1<span style="color: #339933;">&gt;</span>	auto_ptr<span style="color: #339933;">&amp;</span> operator<span style="color: #339933;">=</span><span style="color: #009900;">&#40;</span>auto_ptr<span style="color: #339933;">&lt;</span>_Tp1<span style="color: #339933;">&gt;&amp;</span> __a<span style="color: #009900;">&#41;</span> throw <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		reset<span style="color: #009900;">&#40;</span>__a.<span style="color: #202020;">release</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #b1b100;">return</span> <span style="color: #339933;">*</span>this<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p>/**<br />
* 析构函数，操作很明显，因为是delete，所以auto_ptr是不支持数组指针的，否则会有<br />
* 内存泄露。<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">	~auto_ptr<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		delete _M_ptr<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p>/**<br />
* 解引用操作符<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">		element_type<span style="color: #339933;">&amp;</span> operator<span style="color: #339933;">*</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #993333;">const</span> throw <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		_GLIBCXX_DEBUG_ASSERT<span style="color: #009900;">&#40;</span>_M_ptr <span style="color: #339933;">!=</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #b1b100;">return</span> <span style="color: #339933;">*</span>_M_ptr<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p>/**<br />
* -&gt;操作符，是auto_ptr被用得最多的调用吧。<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">				element_type<span style="color: #339933;">*</span> operator<span style="color: #339933;">-&gt;</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #993333;">const</span> throw <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		_GLIBCXX_DEBUG_ASSERT<span style="color: #009900;">&#40;</span>_M_ptr <span style="color: #339933;">!=</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #b1b100;">return</span> _M_ptr<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p>/**<br />
* 返回auto_ptr管理的指针，这通常用于判断指针是否为空的情况，所以，如果要判断<br />
* auto_ptr管理的指针是否为空，不要使用if(auto_ptr_obj){}而是使用get函数(实际上，<br />
* 因为auto_ptr并没用定义指向element_type的dumb指针的隐式类型转换操作符，所以根本<br />
* 编译不过if(auto_ptr_obj))。<br />
* 但是，auto_ptr并没有禁止你进一步操作你得到的指针，甚至delete它使<br />
* auto_ptr对象内置的指针悬空。<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">	element_type<span style="color: #339933;">*</span> get<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #993333;">const</span> throw <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #b1b100;">return</span> _M_ptr<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p>/**<br />
* 下面的三个函数连带auto_ptr_ref是auto_ptr中的神奇之笔，因为我拍了好多次脑袋才想明白<br />
* 是怎么的应用原理。考虑两种使用情况：<br />
* 1）void foo(auto_ptr&lt; User&gt; ptr);<br />
* 2）auto_ptr&lt; User&gt;  func_returning_auto_ptr(&#8230;..);<br />
*    auto_ptr ptr&lt; User&gt; = func_returning_auto_ptr(&#8230;..);<br />
*  对于第一种情况，当调用方式是：foo(auto_ptr&lt; User&gt;(new User));时，因为是传值调用，<br />
*  而实参是个临时对象，所以需要做赋值构造对象，但auto_ptr的赋值构造函数参数并不是const的<br />
*  所以不匹配其复制构造函数。auto_ptr采用了曲线策略，编译器接着检查类型转换操作符，<br />
*  发现operator auto_ptr_ref&lt;_Tp1&gt;()是匹配的，所以将临时对象转成auto_ptr_ref，再调用<br />
*  auto_ptr(auto_ptr_ref __ref)把auto_ptr_ref转成auto_ptr。<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">	auto_ptr<span style="color: #009900;">&#40;</span>auto_ptr_ref<span style="color: #339933;">&lt;</span>element_type<span style="color: #339933;">&gt;</span> __ref<span style="color: #009900;">&#41;</span> throw <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">:</span>
		_M_ptr<span style="color: #009900;">&#40;</span>__ref._M_ptr<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	template<span style="color: #339933;">&lt;</span>typename _Tp1<span style="color: #339933;">&gt;</span>	operator auto_ptr_ref<span style="color: #339933;">&lt;</span>_Tp1<span style="color: #339933;">&gt;</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> throw <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #b1b100;">return</span> auto_ptr_ref<span style="color: #339933;">&lt;</span>_Tp1<span style="color: #339933;">&gt;</span> <span style="color: #009900;">&#40;</span>this<span style="color: #339933;">-&gt;</span>release<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p>/**<br />
* 和auto_ptr(auto_ptr_ref __ref)相似<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">		auto_ptr<span style="color: #339933;">&amp;</span> operator<span style="color: #339933;">=</span><span style="color: #009900;">&#40;</span>auto_ptr_ref<span style="color: #339933;">&lt;</span>element_type<span style="color: #339933;">&gt;</span> __ref<span style="color: #009900;">&#41;</span> throw <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>__ref._M_ptr <span style="color: #339933;">!=</span> this<span style="color: #339933;">-&gt;</span>get<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			delete _M_ptr<span style="color: #339933;">;</span>
			_M_ptr <span style="color: #339933;">=</span> __ref._M_ptr<span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		<span style="color: #b1b100;">return</span> <span style="color: #339933;">*</span>this<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span></pre></div></div>

<p>/**<br />
* 怎么说这个函数呢？我还不晓得这个转换操作符在什么时候调用呢？<br />
*/</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">			template<span style="color: #339933;">&lt;</span>typename _Tp1<span style="color: #339933;">&gt;</span>	operator auto_ptr<span style="color: #339933;">&lt;</span>_Tp1<span style="color: #339933;">&gt;</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> throw <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #b1b100;">return</span> auto_ptr<span style="color: #339933;">&lt;</span>_Tp1<span style="color: #339933;">&gt;</span> <span style="color: #009900;">&#40;</span>this<span style="color: #339933;">-&gt;</span>release<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

<p>//auto_ptr是不支持void类型的模板特化。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">template<span style="color: #339933;">&lt;&gt;</span> class auto_ptr<span style="color: #339933;">&lt;</span>void<span style="color: #339933;">&gt;</span> <span style="color: #009900;">&#123;</span>
public<span style="color: #339933;">:</span>
	<span style="color: #993333;">typedef</span> <span style="color: #993333;">void</span> element_type<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></div></div>

]]></content:encoded>
			<wfw:commentRss>http://www.kafka0102.com/2010/02/62.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

