<?xml version="1.0" standalone="yes"?>
<?xml-stylesheet type="text/xsl" href="css/rss.xslt"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>ZwqXin - C/C++</title><link>http://www.zwqxin.com/</link><description>    一起学习OPENGL吧 - </description><generator>RainbowSoft Studio Z-Blog 1.8 Arwen Build 90619</generator><language>zh-CN</language><copyright>Copyright 2008-2010 ZwqXin. Some Rights Reserved. Theme edited from ipati. </copyright><pubDate>Fri, 10 Sep 2010 11:25:26 +0800</pubDate><item><title>不要犹豫了，用sstream吧</title><author>a@b.com (zwqxin)</author><link>http://www.zwqxin.com/archives/cpp/use-sstream.html</link><pubDate>Fri, 28 Aug 2009 21:54:32 +0800</pubDate><guid>http://www.zwqxin.com/archives/cpp/use-sstream.html</guid><description><![CDATA[<p>sstream是标准C++里面多么强大多么有用的东西啊，从纷繁的格式字符串处理中稍微解放出来吧~&nbsp; &mdash;&mdash;<a href="http://www.zwqxin.com/">ZwqXin</a>.com</p><p>包含&lt;sstream&gt;，顺便加个使用名字空间std的声明，可以开始了。sstream里包括istringstream、ostringstream、stringstream、stringbuf和各宽字符版本的对象类型。更方便的格式化字符串处理，更安全的类型转换。</p><p style="text-align: center"><img alt="" src="http://www.pconline.com.cn/pcedu/empolder/gj/c/0504/pic/08cppios01.gif" /></p><p>这天还是要做那个机器视觉的编程实验，涉及不少数据的提取和保存。也就是&quot;硬盘文件-&gt;内存数据&quot;、&quot;内存数据-&gt;硬盘文件&quot;这类IO。对我来说，一般会使用的是C时代的FILE* - fprintf - sprintf - sscanf - fread/fwrite，或者C++的fstream - read/write之类的，保存到文件的操作比较简单，那么就来说从文件读入数据了：</p><p style="text-align: center"><table cellspacing="1" cellpadding="1" width="200" border="1">    <tbody>        <tr>            <td style="text-align: center">2</td>            <td style="text-align: center">45.3</td>            <td style="text-align: center">68.2</td>            <td style="text-align: center">36.8</td>        </tr>        <tr>            <td style="text-align: center">7</td>            <td style="text-align: center">77.4</td>            <td style="text-align: center">55.7</td>            <td style="text-align: center">70.1</td>        </tr>        <tr>            <td style="text-align: center">3</td>            <td style="text-align: center">22.0</td>            <td style="text-align: center">79.9</td>            <td style="text-align: center">53.5</td>        </tr>        <tr>            <td style="text-align: center">...</td>            <td style="text-align: center">...</td>            <td style="text-align: center">...</td>            <td style="text-align: center">...</td>        </tr>    </tbody></table></p><p>这样的文件，以前我常常这样做：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">char</span><span>&nbsp;Buffer[50]&nbsp;=&nbsp;{0}; </span></span></li>    <li>&nbsp;</li>    <li class="alt"><span>ifstream&nbsp;infile;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="comment">//ifstream&nbsp;infile(filename); </span></li>    <li><span>infile.open(filename,&nbsp;ios::</span><span class="keyword">in</span><span>);&nbsp;&nbsp;</span><span class="comment">// </span></li>    <li class="alt">&nbsp;</li>    <li><span class="keyword">int</span><span>&nbsp;DATA1; </span></li>    <li class="alt"><span class="keyword">float</span><span>&nbsp;DATA2,&nbsp;DATA3,&nbsp;DATA4; </span></li>    <li><span class="keyword">char</span><span>&nbsp;c; </span></li>    <li class="alt"><span class="keyword">while</span><span>(infile.</span><span class="keyword">get</span><span>(c)) </span></li>    <li><span>{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(c==0x00&nbsp;||&nbsp;c&nbsp;==&nbsp;</span><span class="string">'\n'</span><span>&nbsp;&nbsp;||&nbsp;c&nbsp;==</span><span class="string">'\t'</span><span>)</span><span class="keyword">continue</span><span>; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;infile.seekg(-1,&nbsp;ios::cur); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;infile.getline(Buffer,&nbsp;50); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;sscanf(Buffer,&nbsp;</span><span class="string">&quot;%d&nbsp;&nbsp;%f&nbsp;%f&nbsp;%f&nbsp;&quot;</span><span>, </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&amp;DATA1,&nbsp;&amp;DATA2,&nbsp;&amp;DATA3,&nbsp;&amp;DATA4); </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DATA1list.push_back(DATA1);&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DATA2list.push_back(DATA2);&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DATA3list.push_back(DATA3);&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DATA4list.push_back(DATA4); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li>&nbsp;</li>    <li class="alt"><span>} </span></li>    <li>&nbsp;</li>    <li class="alt"><span>infile.close();</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>	char Buffer[50] = {0};	ifstream infile;    //ifstream infile(filename);	infile.open(filename, ios::in);  //	int DATA1;	float DATA2, DATA3, DATA4;	char c;	while(infile.get(c))	{		if(c==0x00 || c == '\n'  || c =='\t')continue;		else		{		infile.seekg(-1, ios::cur);		infile.getline(Buffer, 50);		sscanf(Buffer, &quot;%d  %f %f %f &quot;,		&amp;DATA1, &amp;DATA2, &amp;DATA3, &amp;DATA4);        DATA1list.push_back(DATA1);         DATA2list.push_back(DATA2);         DATA3list.push_back(DATA3);         DATA4list.push_back(DATA4);		}	}	infile.close();</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>基本上就是字面的意思。但是，这个样子经常会出错，特别是格式化字符那里，又有多少人铭记double类型的格式化字符是%lf而不是%f呢&hellip;&hellip;用sstream，就可以这样做：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="comment">// </span></span></li>    <li><span>ifstream&nbsp;infile;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>infile.open(filename,&nbsp;ios::</span><span class="keyword">in</span><span>);&nbsp; </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">int</span><span>&nbsp;DATA1; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">float</span><span>&nbsp;DATA2,&nbsp;DATA3,&nbsp;DATA4; </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;stringstream&nbsp;sem; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;sem&nbsp;&lt;&lt;&nbsp;infile.rdbuf(); </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">while</span><span>(</span><span class="keyword">true</span><span>) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sem&nbsp;&gt;&gt;&nbsp;DATA1; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(sem.eof())</span><span class="keyword">break</span><span>; </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sem&nbsp;&gt;&gt;&nbsp;DATA2; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sem&nbsp;&gt;&gt;&nbsp;DATA3; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sem&nbsp;&gt;&gt;&nbsp;DATA4; </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DATA1list.push_back(DATA1); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DATA2list.push_back(DATA2); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DATA3list.push_back(DATA3); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DATA4list.push_back(DATA4); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt"><span>infile.close(); </span></li>    <li>&nbsp;</li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>//ifstream infile;    infile.open(filename, ios::in); 	int DATA1;	float DATA2, DATA3, DATA4;	stringstream sem;	sem &lt;&lt; infile.rdbuf();	  while(true)	  {		sem &gt;&gt; DATA1;		if(sem.eof())break;		sem &gt;&gt; DATA2;		sem &gt;&gt; DATA3;		sem &gt;&gt; DATA4;		DATA1list.push_back(DATA1);		DATA2list.push_back(DATA2);		DATA3list.push_back(DATA3);		DATA4list.push_back(DATA4);	  }infile.close();</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p style="text-align: left">1. fstream对象的rdbuf()成员函数返回的是一个std::filebuf*指针，用以重定向流，结果就是文件的内容全部作为一个C++流，流向我们的stringstream对象里。</p><p style="text-align: left">2.我们从该stringstream对象里从头取出各个数据到中间变量DATAi中，这可不是一个一个字符取哦，因为它是以空格或者\n,\t之类字符为分隔的，所以很方便；</p><p style="text-align: left">3.同时，这里取出到DATA的也不是字符串，而确实就是连续一个int三个float哦，因为内部有安全的转换机制，所以不要再担心C那种格式化的问题了，无论是INT还是DOUBLE还是CHAR*都能做到。这是stringstream最强大之处。</p><p style="text-align: left">4.判断&ldquo;取&rdquo;的动作结束的eof()为什么要在最后做了一次&ldquo;无用功&rdquo;后才判断呢？因为只有这样能判断。stringstream这时候扮演着一个乐施者，他不会去数自己包包里还剩下多少块波板糖，只会不停伸手入包里取出糖，而只有最后一次伸手入包包发现已经没糖了&mdash;&mdash;所以才叫最后一次&mdash;&mdash;才会醒觉：哦，原来我一贫如洗了。</p><p style="text-align: left">基本上太强了。再举例，这次是保存数据到文件，虽是简单，但如果按我以前用fstream文件流的方法，效果比C的fwrite要糟糕：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">char</span><span>&nbsp;Buffer[50]&nbsp;=&nbsp;{0}; </span></span></li>    <li>&nbsp;</li>    <li class="alt"><span>ofstream&nbsp;fileSave; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;fileSave.open(filename,ios::ate); </span></li>    <li class="alt">&nbsp;</li>    <li><span class="keyword">for</span><span>(</span><span class="keyword">int</span><span>&nbsp;i&nbsp;=&nbsp;0&nbsp;;&nbsp;i&nbsp;&lt;&nbsp;Num;&nbsp;i++) </span></li>    <li class="alt"><span>{ </span></li>    <li><span>&nbsp;sprintf(Buffer,&nbsp;</span><span class="string">&quot;%d&nbsp;&nbsp;%.2f&nbsp;%.2f&nbsp;%.2f&nbsp;&quot;</span><span>, </span></li>    <li class="alt"><span>DATA1[i],&nbsp;DATA2[i],&nbsp;DATA3[i],&nbsp;DATA4[i]); </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;fileSave.write(Buffer,</span><span class="keyword">sizeof</span><span>(</span><span class="string">&quot;00&nbsp;&nbsp;00.00&nbsp;00.00&nbsp;00.00&quot;</span><span>)); </span></li>    <li><span>} </span></li>    <li class="alt"><span>&nbsp;&nbsp;fileSave.close;</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>	char Buffer[50] = {0};	ofstream fileSave;    fileSave.open(filename,ios::ate);	for(int i = 0 ; i &lt; Num; i++)	{	 sprintf(Buffer, &quot;%d  %.2f %.2f %.2f &quot;,	DATA1[i], DATA2[i], DATA3[i], DATA4[i]);	 fileSave.write(Buffer,sizeof(&quot;00  00.00 00.00 00.00&quot;));	}	  fileSave.close;</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p style="text-align: left"><span class="keyword">问题在sizeof</span><span>(</span><span class="string">&quot;00&nbsp;&nbsp;00.00&nbsp;00.00&nbsp;00.00&quot;</span><span>)这里，我没办法确定真实数据的大小。事实上原始数据某组是（3 21.52 63.31 22.22 1.36）或者（320 25.36 54.322 72.31 32.101）都会很麻烦&mdash;&mdash;一般是尽量让这个size大点，不要让它把后面的吃了，但过小的话后面就会出现其他多余的字符，或者每组数据不对齐造成文件不雅观(用strlen()就是另话了)。用stringstream的话，我只想说：很文雅：</span></p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="comment">// </span></span></li>    <li><span>&nbsp;&nbsp;&nbsp;ofstream&nbsp;fileSave;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;fileSave.open(filename,ios::ate);&nbsp; </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;ostringstream&nbsp;strStream; </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;strStream.setf(ios::showpoint); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;strStream&nbsp;&lt;&lt;&nbsp;std::setprecision(4); </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>(</span><span class="keyword">int</span><span>&nbsp;i&nbsp;=&nbsp;0&nbsp;;&nbsp;i&nbsp;&lt;&nbsp;Num;&nbsp;i++)&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;{&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strStream&lt;&lt;DATA1[i]&lt;&lt;</span><span class="string">&quot;&nbsp;&nbsp;&quot;</span><span>&nbsp;&lt;&lt;DATA2[i]&lt;&lt;&nbsp;</span><span class="string">&quot;&nbsp;&nbsp;&quot;</span><span>&lt;&lt;DATA3[i]&lt;&lt;&nbsp;</span><span class="string">&quot;&nbsp;&nbsp;&quot;</span><span>&lt;&lt;DATA4[i]&lt;&lt;endl; </span></li>    <li class="alt"><span>&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fileSave.write(&nbsp;strStream.str().c_str(),&nbsp;strStream.str().size()&nbsp;); </span></li>    <li class="alt"><span>&nbsp;&nbsp;} </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;fileSave.close;</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>//   ofstream fileSave;    fileSave.open(filename,ios::ate); 	ostringstream strStream;	strStream.setf(ios::showpoint);	strStream &lt;&lt; std::setprecision(4);   for(int i = 0 ; i &lt; Num; i++)   {      strStream&lt;&lt;DATA1[i]&lt;&lt;&quot;  &quot; &lt;&lt;DATA2[i]&lt;&lt; &quot;  &quot;&lt;&lt;DATA3[i]&lt;&lt; &quot;  &quot;&lt;&lt;DATA4[i]&lt;&lt;endl;      fileSave.write( strStream.str().c_str(), strStream.str().size() );  }  fileSave.close;</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>setprecision(4)是有效数字位数，所以数据不会难看（当然小数位数也是可设定的），配合strStream&lt;&lt;DATA1[i]&lt;&lt;<span class="string">&quot;&nbsp;&nbsp;&quot;</span><span>&nbsp;&hellip;&hellip;这样的&ldquo;流&rdquo;式输出，感觉太好了。最后write的参数就是strStream内容转为std::string后，其大小以及再转化为char*所得的底层字符串地址了。太好了。</span></p><p><span>我不太研究STD里的效率问题，所以我不能给出效率方面的建议，但是，即使效率有小小损失，抵得过它的方便吗？以前基本用fprint,sscanf现在想多用stringstream了；以前是char*用得多、MFC也用CString，但现在觉得标准C++的string其实也很可爱的；以前是atoi,atof，然后sprintf做f-to-char,i-to-char(a)，现在知道stringstream才是这类转换的高手哈。</span></p><p><span>最后提下，清空一个stringstream对象(stringstream strs)里的内容时，单strs.clear()是不行的，要用strs.str(&quot;&quot;)这句才行哦。最后最后，写程序时写出的一段代码，给有缘人参考，多多指教：</span></p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt">&nbsp;</li>    <li><span class="comment">//把整个文件内容存储到单独一个字符串里：&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;ofstream&nbsp;dfileSave;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;stringstream&nbsp;outstream;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;dfileSave.open(</span><span class="string">&quot;tResult.txt&quot;</span><span>,&nbsp;&nbsp;ios::</span><span class="keyword">in</span><span>&nbsp;|&nbsp;ios::app);&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;std::filebuf&nbsp;*pFileBuffer&nbsp;=&nbsp;&nbsp;dfileSave.rdbuf();&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;outstream&nbsp;&lt;&lt;&nbsp;pFileBuffer;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">int</span><span>&nbsp;filesize&nbsp;=&nbsp;pFileBuffer-&gt;pubseekoff(0,&nbsp;ios::end,&nbsp;ios::</span><span class="keyword">in</span><span>);&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;std::</span><span class="keyword">string</span><span>&nbsp;sre&nbsp;=&nbsp;outstream.str();&nbsp;</span><span class="comment">//1&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">const</span><span>&nbsp;</span><span class="keyword">char</span><span>&nbsp;*de&nbsp;=&nbsp;sre.c_str();&nbsp;&nbsp;</span><span class="comment">//2&nbsp; </span></li>    <li><span>&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;dfileSave.close();&nbsp; </span></li>    <li>&nbsp;</li>    <li class="alt"><span class="comment">//ZwqXin:&nbsp;拜拜~！</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>//把整个文件内容存储到单独一个字符串里：     ofstream dfileSave;     stringstream outstream;       dfileSave.open(&quot;tResult.txt&quot;,  ios::in | ios::app);       std::filebuf *pFileBuffer =  dfileSave.rdbuf();       outstream &lt;&lt; pFileBuffer;       int filesize = pFileBuffer-&gt;pubseekoff(0, ios::end, ios::in);       std::string sre = outstream.str(); //1     const char *de = sre.c_str();  //2       dfileSave.close(); //ZwqXin: 拜拜~！</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div>]]></description><category>C/C++</category><comments>http://www.zwqxin.com/archives/cpp/use-sstream.html#comment</comments><wfw:comment>http://www.zwqxin.com/</wfw:comment><wfw:commentRss>http://www.zwqxin.com/feed.asp?cmt=74</wfw:commentRss><trackback:ping>http://www.zwqxin.com/cmd.asp?act=tb&amp;id=74&amp;key=f21b065b</trackback:ping></item><item><title>子类调用父类的纯虚函数之问题</title><author>a@b.com (zwqxin)</author><link>http://www.zwqxin.com/archives/cpp/pure-virtual-between-derivation.html</link><pubDate>Mon, 08 Jun 2009 23:18:32 +0800</pubDate><guid>http://www.zwqxin.com/archives/cpp/pure-virtual-between-derivation.html</guid><description><![CDATA[<p>今天遇上的一点C++问题,记录下来备忘一下。父类是个抽象类，子类却对它有些特别的需求&mdash;&mdash;子类调用父类的纯虚函数。（什么？大逆不道？）&mdash;&mdash;<a href="http://www.zwqxin.com/">ZwqXin</a>.com</p><p>有这么一个库，库里有个工具类，不妨认为是一个关于listbox操作的类吧（事实上就是了- -）。它通过头文件提供接口给我们使用，而把实现藏进dll里面了(或者是lib里面)。这个类有个特点：除了构造析构函数之外的都是纯虚函数。也就是说，这完全就是一个&ldquo;接口(interface)&rdquo;概念的类。</p><p>纯虚函数的概念就不多解释了。它的代名词就是&ldquo;抽象&rdquo;，尽管你不一定认为它有多么需要抽象。</p><p>现在，我觉得这个工具类（姑且叫BaseListBox）不够用，想从中派生出一个更有内涵的子类（不妨叫DerivedListBox）。问题来了，这不是MFC，没有像MFC那样预先考虑让人随便重载界面类。面对这个完全&ldquo;虚&rdquo;了的BaseListBox，我能想到的重载方式&mdash;&mdash;没啥，把这些函数全部&ldquo;重载&rdquo;吧。</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">class</span><span>&nbsp;BaseListBox </span></span></li>    <li><span>{ </span></li>    <li class="alt"><span class="keyword">public</span><span>: </span></li>    <li><span>&nbsp;&nbsp;&nbsp;BaseListBox(){} </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;</span><span class="keyword">virtual</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;func1()=0; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;</span><span class="keyword">virtual</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;func2()=0; </span></li>    <li><span>}; </span></li>    <li class="alt">&nbsp;</li>    <li><span class="keyword">class</span><span>&nbsp;DerivedListBox&nbsp;:&nbsp;</span><span class="keyword">public</span><span>&nbsp;BaseListBox </span></li>    <li class="alt"><span>{ </span></li>    <li><span class="keyword">public</span><span>: </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;DerivedListBox()&nbsp;:&nbsp;BaseListBox(){} </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;</span><span class="keyword">void</span><span>&nbsp;NewFunc(){}&nbsp; </span></li>    <li><span>&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;</span><span class="keyword">virtual</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;func1(){}; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;</span><span class="keyword">virtual</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;func2(){}; </span></li>    <li class="alt"><span>}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>class BaseListBox<br/>{<br/>public:<br/>   BaseListBox(){}<br/><br/>   virtual void func1()=0;<br/>   virtual void func2()=0;<br/>};<br/><br/>class DerivedListBox : public BaseListBox<br/>{<br/>public:<br/>   DerivedListBox() : BaseListBox(){}<br/><br/>  void NewFunc(){} <br/> <br/>   virtual void func1(){};<br/>   virtual void func2(){};<br/>}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>实际上该工具类也没成员函数，因此用的是默认析构(因为它也是继承来的,这个与本主题无关,就不待多言了，但继承体系的底部有个虚函数析构是时刻有必要提醒的)。 ~简化一下，用上面的代码说明问题好了。当然，事实上这种funX()的函数有N个....</p><p>如何维护&ldquo;子类就是父类&rdquo;这个说法呢？在代码部分变通一下：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">class</span><span>&nbsp;DerivedListBox&nbsp;&nbsp;:&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;BaseListBox </span></span></li>    <li><span>{&nbsp; </span></li>    <li class="alt"><span class="keyword">public</span><span>:&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;DerivedListBox()&nbsp;:&nbsp;BaseListBox(){}&nbsp; </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;</span><span class="keyword">void</span><span>&nbsp;NewFunc(){}&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;</span><span class="keyword">virtual</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;func1(){&nbsp;</span><span class="keyword">return</span><span>&nbsp;BaseListBox::func1()&nbsp;};&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;</span><span class="keyword">virtual</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;func2(){&nbsp;</span><span class="keyword">return</span><span>&nbsp;BaseListBox::func2()&nbsp;};&nbsp; </span></li>    <li><span>}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>class DerivedListBox  :  public BaseListBox<br/>{ <br/>public: <br/>   DerivedListBox() : BaseListBox(){} <br/><br/>  void NewFunc(){}  <br/>  <br/>   virtual void func1(){ return BaseListBox::func1() }; <br/>   virtual void func2(){ return BaseListBox::func2() }; <br/>}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>啥米？调用父类的纯虚函数？！那不是纯虚函数吗？那不是接口吗？呃....没什么好惊奇的。原来的类能给你用，就说明了这些接口真的有代码实现部分。（至于原来是怎么被用的，自个儿想想猜猜吧。）不过这段实现不能用于继承体系，注定了与动态调用无缘。在这里funX函数通过静态方式调用了父类的同名纯虚函数，驶离了&ldquo;无法实例化&rdquo;的魔咒。BaseListBox作为&ldquo;限定符&rdquo;意味着&ldquo;要调用的的确就是BaseListBox的那个funcX&rdquo;，如同通过场景唯一的ID访问场景节点对象[访问游戏怪物]，没有什么可以作为意外的。</p><p>所以，注意，C++里纯虚函数也可以有默认实现！</p><p>但是，VS2005里抛出了这种链接错误：<br /><br/>&nbsp;error LNK2001: 无法解析的外部符号 &quot;public: virtual void__thiscall BaseListBox::func1()&nbsp;&quot; <br /><br/>(<a href="mailto:?.......@@UBEPB_WI@Z">?.......@@UBEPB_WI@Z</a>).........fatal error LNK1120: 1 个无法解析的外部命令<br /><br/>&mdash;&mdash;<a href="http://www.zwqxin.com/">ZwqXin</a>.com</p><p>改为((BaseListBox*)this)-&gt;func1()倒是过了链接，但是同样是运行终止的。</p><p>无理解错的话，应该是因为编译器找不到该BaseListBox::func1()的实现（默认纯虚实现部分）。链接DLL什么的我真是云里雾里，不过反正就是&ldquo;无法找到&rdquo;（或者说&ldquo;不认为找到了&rdquo;更合适）。我简单地做过实验，如果BaseListBox的实现是确实在一个实现文件(.cpp)里的话，确实上面这样的代码是能够编译和运行的。</p><p>在<a target="_blank" href="http://techplex.spaces.live.com/Blog/cns!C49D0FCC71043976!182.entry">[C++语言相关]为什么需要工厂(Factory)</a>一文中，根据作者所言，大概也知道在类库里面接口和实现有可能不是那么简单地相连的，编译器说&ldquo;不认识&rdquo;自然有它的苦衷，我不认为这次也是因为它坏掉了。（好心人可详细解释一下吗？）</p><p>因此，有个折中的坏主意：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">class</span><span>&nbsp;BaseListBox&nbsp; </span></span></li>    <li><span>{&nbsp; </span></li>    <li class="alt"><span class="keyword">public</span><span>:&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;BaseListBox(){}&nbsp; </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;</span><span class="keyword">virtual</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;func1()=0;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;</span><span class="keyword">virtual</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;func2()=0;&nbsp; </span></li>    <li><span>};&nbsp; </span></li>    <li class="alt">&nbsp;</li>    <li><span class="keyword">class</span><span>&nbsp;DerivedListBox&nbsp;:&nbsp;</span><span class="keyword">public</span><span>&nbsp;BaseListBox&nbsp; </span></li>    <li class="alt"><span>{&nbsp; </span></li>    <li><span class="keyword">public</span><span>:&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;DerivedListBox(BaseListBox&nbsp;&nbsp;*listbox) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:&nbsp;baseListbox(listbox),&nbsp;&nbsp;BaseListBox(){}&nbsp; </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;</span><span class="keyword">void</span><span>&nbsp;NewFunc(){}&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;</span><span class="keyword">virtual</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;func1(){&nbsp;baseListbox-&gt;&nbsp;func1()&nbsp;};&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;</span><span class="keyword">virtual</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;func2(){&nbsp;baseListbox-&gt;&nbsp;func2()&nbsp;};&nbsp; </span></li>    <li>&nbsp;</li>    <li class="alt"><span class="keyword">private</span><span>: </span></li>    <li><span>&nbsp;&nbsp;&nbsp;BaseListBox&nbsp;&nbsp;*baseListbox; </span></li>    <li class="alt"><span>}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>class BaseListBox <br/>{ <br/>public: <br/>   BaseListBox(){} <br/><br/>   virtual void func1()=0; <br/>   virtual void func2()=0; <br/>}; <br/><br/>class DerivedListBox : public BaseListBox <br/>{ <br/>public: <br/>   DerivedListBox(BaseListBox  *listbox)<br/>     : baseListbox(listbox),  BaseListBox(){} <br/><br/>  void NewFunc(){}  <br/>  <br/>   virtual void func1(){ baseListbox-&gt; func1() }; <br/>   virtual void func2(){ baseListbox-&gt; func2() }; <br/><br/>private:<br/>   BaseListBox  *baseListbox;<br/>}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>这种手法的重点应该落在baseListbox这个成员指针的&ldquo;值&rdquo;之问题上。的确，库里面BaseListBox本来就不是为了作为基类而使用的（或者我改回IListBox更不容易招见误会），库通过其他手段能直接创建一个真正的ListBox界面控件，返回其BaseListBox*指针。（或许前面的错误与此也有关。）这样，通过DerivedListBox的构造函数传入此指针后，就如同DerivedListBox 作为一个可装单一物件的容器与BaseListBox关联。这样一来，其实继承也没什么用处了。</p><p>或许改名IListBoxContainer更适合，但是我仍然想让该DerivedListBox像一个真正的&ldquo;Derived-ListBox &rdquo;，于是把BaseListBox里面的纯虚函数都通过这种&ldquo;呼叫&rdquo;的方式，在每个同名函数里通过BaseListBox*指针调用。不抹去&ldquo;:&nbsp;<span class="keyword">public</span><span>&nbsp;BaseListBox&nbsp; &rdquo;也是为了让继承体系更底端的函数也&ldquo;有效&rdquo;。</span></p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">int</span><span>&nbsp;main() </span></span></li>    <li><span>{ </span></li>    <li class="alt"><span class="comment">//必须有种能产生有效BaseListBox指针的方式 </span></li>    <li><span>&nbsp;BaseListBox&nbsp;*olistbox&nbsp;=&nbsp;xx-&gt;createyy(); </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;DerivedListBox&nbsp;*listbox&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;DerivedListBox&nbsp;(olistbox&nbsp;); </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;listbox-&gt;func1(); </span></li>    <li class="alt"><span>&nbsp;&nbsp;listbox-&gt;func2(); </span></li>    <li><span>&nbsp;&nbsp;listbox-&gt;NewFunc(); </span></li>    <li class="alt">&nbsp;</li>    <li><span class="keyword">return</span><span>&nbsp;</span><span class="keyword">false</span><span>; </span></li>    <li class="alt"><span>}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>int main()<br/>{<br/>//必须有种能产生有效BaseListBox指针的方式<br/> BaseListBox *olistbox = xx-&gt;createyy();<br/><br/>  DerivedListBox *listbox = new DerivedListBox (olistbox );<br/><br/>  listbox-&gt;func1();<br/>  listbox-&gt;func2();<br/>  listbox-&gt;NewFunc();<br/><br/>return false;<br/>}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p><span>总之，在一定场合这种坏主意还是能帮忙的。</span></p><p><span>感谢你的指导与收看，这里是<a href="http://www.zwqxin.com/">ZwqXin</a>的<a target="_blank" href="http://www.zwqxin.com/">3D编程事件记录簿</a>，谢谢莅临，意见可在<a target="_self" href="http://www.zwqxin.com/archives/cpp/pure-virtual-between-derivation.html">本页面评论</a>或光临<a target="_blank" href="http://www.zwqxin.com/guestbook.asp">舍下的留言簿</a>。</span></p><p><span>阁下对此问题有更好的处理方式吗？可否不吝口水告之呢？</span></p>]]></description><category>C/C++</category><comments>http://www.zwqxin.com/archives/cpp/pure-virtual-between-derivation.html#comment</comments><wfw:comment>http://www.zwqxin.com/</wfw:comment><wfw:commentRss>http://www.zwqxin.com/feed.asp?cmt=57</wfw:commentRss><trackback:ping>http://www.zwqxin.com/cmd.asp?act=tb&amp;id=57&amp;key=15ddd415</trackback:ping></item><item><title>WINDOWS游戏编程大师技巧笔记之——学海无崖</title><author>a@b.com (zwqxin)</author><link>http://www.zwqxin.com/archives/cpp/windows-game-programer-1.html</link><pubDate>Tue, 10 Mar 2009 19:35:17 +0800</pubDate><guid>http://www.zwqxin.com/archives/cpp/windows-game-programer-1.html</guid><description><![CDATA[<p>&nbsp;从图书馆借来一本厚厚的WINDOWS游戏编程大师技巧笔记，看一看，见识见识，写写感想。今为第一章（学海无崖）之感想，较有启发的，作者在这里谈了谈它对C++之于游戏的一点点所谓&ldquo;规范&rdquo;&mdash;&mdash;<a href="http://www.zwqxin.com/">ZwqXin</a>.com</p><p><strong>1.不要怕使用全局变量</strong></p><p>貌似效率更高？可是......可是........好吧，真不知谁说得对。我最初的OpenGL程序其实是用纯粹C编写的，说纯粹C貌似不好吧（我对C之精髓真是...恩，遥望），过程式结构吧。过程式就算了，还一直只会用全局变量...（原谅我吧，当时连&ldquo;类&rdquo;是啥米都不知道，虽然一直用的框架的CMainFrame就是一个类 - -）。这或许受点NEHE的初级教程例程的影响（喂，别推卸责任喔~），倒是，为何NEHE不用类封装呢？搞那么多全局函数。学C++后回头看它，以为是为了方便（OpenGL例程嘛，又不是C++例程~~），看来是不是NEHE大师也跟本书作者领悟一样呢？</p><p>恩....反正我被PI后，也很少胆敢用全局变量了（倒是有时候随便设些测试用的变量，用完也就删了...），当然，事有必须的 时候...我还喜欢用全局函数全局类呢....唉不多说了。</p><p><strong>2.使用内联函数</strong></p><p>这个....我也时常用呢。尤其看了Effective C++之后.但是权衡起得失来也经常有&quot;犹豫不决&quot;的时候.反正如果实现太长我就不敢内联了。</p><p><strong>3.尽量使用32位变量，而不用8位16位的</strong></p><p>唉，可能日后要改成&ldquo;尽量使用64位变量&rdquo;呢，谁知道呢。提倡用32位变量是原于内存中存储数据的&ldquo;对齐&rdquo;问题，Effective C++在条款50左右也提过，不过那是另一回事了。Pentium处理器全是32位的，意味它不喜欢非32位的（喂，现在呢~），寻址的不规则可能会影响效率。书中举例了：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">struct</span><span>&nbsp;CPOINT </span></span></li>    <li><span>{ </span></li>    <li class="alt"><span class="keyword">short</span><span>&nbsp;x,y; </span></li>    <li><span>unsigned&nbsp;</span><span class="keyword">char</span><span>&nbsp;c; </span></li>    <li class="alt"><span>} </span></li>    <li><span class="keyword">struct</span><span>&nbsp;CPOINT </span></li>    <li class="alt"><span>{ </span></li>    <li><span class="keyword">int</span><span>&nbsp;x,y; </span></li>    <li class="alt"><span class="keyword">int</span><span>&nbsp;c; </span></li>    <li><span>}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>struct CPOINT<br/>{<br/>short x,y;<br/>unsigned char c;<br/>}<br/>struct CPOINT<br/>{<br/>int x,y;<br/>int c;<br/>}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>或许你会抱同样的看法：下面的比较好。完全是因为审美观问题。但是问及效率，我还真答不上来（另外，我也忘记一个<span class="keyword">short究竟占多少位了 - -</span>）。上面的CPOINT共占（ 2*sizeof(short)+sizeof(char)=）5字节。不要说2的某次方了，还是个奇数......立正！对齐！向前看！</p><p>然后作者告诉最后填充一下自定义的结构为32字节的倍数........晕。</p><p><strong>4.注释代码</strong></p><p>好，灰常好。可惜某Zwqxin是个不好的孩子。</p><p><strong>5.RISC尽量简化代码</strong></p><p>通常来说，我也不喜欢一行里充满复合指令。也是的，何必为难编译器呢（虽然它是微软的东东！）......好处：方便设置断点（非常&ldquo;有用&rdquo;的好处）！让处理器有机会用更多执行单元并行处理更多的代码！</p><p><strong>6.使用二进制移位运算来进行乘数，除数为2的简单乘除法</strong></p><p>呃....这个...考虑....考虑.........永远地考虑ING呵呵</p><p><strong>7.设计高效率算法</strong></p><p>&ldquo;更好的做法是使用清楚、高效率的算法而不是蛮力和穷举型的算法。&rdquo;（碎碎念：要是上苍公平点人人都有这样的能力这样的头脑，谁不用呢！）</p><p><strong>8.不要在编程过程中优化代码（否则就通常是在浪费时间），建议留后完成</strong></p><p>虽然深感同意，但是自问自己也没啥&ldquo;在编码过程中就优化&rdquo;的能耐......注意，要日后还能看明白自己写的是啥，代码起码写的时候要条理清晰哦。</p><p><strong>9.不要为简单的对象定义太多复杂的数据结构</strong></p><p>这个....貌似我对数据结构就是蒙....</p><p><strong>10.简单而直观的代码是最好的程序</strong></p><p>设计模式迷哥迷姐（？）们快去轰他，哈哈，作者叫Andre LaMothe，邮箱<a href="mailto:ceo@xgames3d.com">ceo@xgames3d.com</a>（被爆了没？）</p><p><strong>11.避免一错再错，沿着死路走</strong></p><p>这是很好的忠告。但是，如果能告诉我判断怎样的路是死路的方法就更好了。</p><p><strong>12.经常备份你的工作</strong></p><p>这是很必要的嘛。自己深有同感，有时候就是想回到走错路之前那一步，可惜再回去之前，眼前的这程序却玩起了崩溃。况且备份一次代价太高了TAT，珍惜硬盘呀TAT（摊手：给一个能够一键备份程序必要文件的东东我吧~~）；再况且，CODER不是都不太喜欢&ldquo;重复的代码&rdquo;么呵呵。</p><p><strong>13.开始工作前，进行一下组织工作，使用合理的文件名目录名，提出一致的名民约定。资源分目录。</strong></p><p>好了。第一章，&ldquo;血海乌鸦&rdquo;就是这样。其余的....没什么了吧？</p><p style="text-align: right">参考：WINDOWS游戏编程大师技巧 2nd edition</p>]]></description><category>C/C++</category><comments>http://www.zwqxin.com/archives/cpp/windows-game-programer-1.html#comment</comments><wfw:comment>http://www.zwqxin.com/</wfw:comment><wfw:commentRss>http://www.zwqxin.com/feed.asp?cmt=39</wfw:commentRss><trackback:ping>http://www.zwqxin.com/cmd.asp?act=tb&amp;id=39&amp;key=599f08f4</trackback:ping></item><item><title>伺机而动，设计模式之观察者(Observer)</title><author>a@b.com (zwqxin)</author><link>http://www.zwqxin.com/archives/cpp/learn-design-pattern-obsever.html</link><pubDate>Mon, 02 Mar 2009 22:20:58 +0800</pubDate><guid>http://www.zwqxin.com/archives/cpp/learn-design-pattern-obsever.html</guid><description><![CDATA[<p>今天看了BOB大叔的《敏捷软件开发》一书中，关于观察者模式的实现讨论，颇有感慨，于是有此文。&mdash;&mdash;<a href="http://www.zwqxin.com/">ZwqXin</a>.com</p><p>最早接触设计模式是通过国内那本《大话设计模式》吧，通俗易懂地介绍了GOF的23个经典设计模式，可是我看后就忘了TAT，归根还是实践太少。BOB大叔此书讲述的模式不全，而且杂七杂八的也有些在世界领域少言及（乃至我甚至有时认为是不是大叔原创的- -），但是模式的讲述间传递着这么个思想：咱们模式不是本书主角。的确，模式通常只为了传递软件设计思想，而其本身不想成为思想&mdash;&mdash;每本关于设计模式的书籍的作者大概也时不时在书中提及一下这种理念，但他们明显只是为了洗清自己对读者错误引导的嫌疑而已。《敏捷软件开发》一书中的模式只是为了给之后的案例做准备，它不关于设计模式因此无义务给你详解一个一个的模式。但相信读完此书后，你能谅解模式，你不再把它当作明星，而可能是当作朋友。</p><p>设计模式之观察者(Observer)一章是我到现在为止所见的最真诚的设计模式介绍。从一个设计任务开始，思考，重构，最后成其模式。就结果来说我相信是观察者模式的样式中最简单的，简单到可以让人直观其本质，当然这多得BOB大叔的耐心讲解。好了，来看看我看书后顿生的歪主意：</p><p>一场射击比赛中，观众霸好头位，准备为他们相中的设射击手呐喊助威。于是，我想到了一个射击手类（shooter）和一个观众类（Audience）。故事围绕一个射击手和三名观众。我假设射击手射中与否是随机事件，用随机函数rand()来决定两种状态。shoot行为引发状态的改变附加一句射击手的胡言乱语，观众看到射击结果后作出反应：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="preprocessor">#include&nbsp;&quot;stdafx.h&quot; </span></span></li>    <li><span>#include&nbsp;&lt;cstdlib&gt; </span></li>    <li class="alt"><span>#include&nbsp;&quot;windows.h&quot; </span></li>    <li>&nbsp;</li>    <li class="alt"><span class="keyword">class</span><span>&nbsp;Shooter </span></li>    <li><span>{ </span></li>    <li class="alt"><span class="keyword">private</span><span>: </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">enum</span><span>{BINGO,&nbsp;TAT}; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">int</span><span>&nbsp;state; </span></li>    <li><span class="keyword">public</span><span>: </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;Shooter(){state&nbsp;=&nbsp;TAT;&nbsp;}; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">void</span><span>&nbsp;Shoot(</span><span class="keyword">int</span><span>&nbsp;random) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(random%2&nbsp;==&nbsp;0){state&nbsp;=&nbsp;BINGO;&nbsp;printf(</span><span class="string">&quot;射手：好野！打中了！\n&quot;</span><span>);&nbsp;} </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span><span>&nbsp;{state&nbsp;=&nbsp;TAT;&nbsp;printf(</span><span class="string">&quot;射手：5555！打不中！\n&quot;</span><span>);&nbsp;} </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">int</span><span>&nbsp;GetState(){</span><span class="keyword">return</span><span>&nbsp;state;} </span></li>    <li><span>}; </span></li>    <li class="alt">&nbsp;</li>    <li><span class="keyword">class</span><span>&nbsp;Audience </span></li>    <li class="alt"><span>{ </span></li>    <li><span class="keyword">private</span><span>: </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">enum</span><span>{WOW,&nbsp;DAMN}; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">int</span><span>&nbsp;state; </span></li>    <li class="alt"><span class="keyword">public</span><span>: </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Audience(</span><span class="keyword">int</span><span>&nbsp;_state){&nbsp;state&nbsp;=&nbsp;_state;&nbsp;} </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">void</span><span>&nbsp;Reaction() </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(state&nbsp;==&nbsp;DAMN){&nbsp;printf(</span><span class="string">&quot;观众：咦~~有无搞错啊，甘都打5中~~~食sh*t啦你~\n\n&quot;</span><span>);&nbsp;} </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span><span>&nbsp;</span><span class="keyword">if</span><span>(state&nbsp;==&nbsp;WOW){printf(</span><span class="string">&quot;观众：哇，打中了！好犀利啊~~~奖旧sh*t比你食喇~\n\n&quot;</span><span>);&nbsp;} </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li>&nbsp;</li>    <li class="alt"><span>}; </span></li>    <li>&nbsp;</li>    <li class="alt"><span class="keyword">int</span><span>&nbsp;main(</span><span class="keyword">int</span><span>&nbsp;argc,&nbsp;</span><span class="keyword">char</span><span>*&nbsp;argv[]) </span></li>    <li><span>{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;srand(GetTickCount()); </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;Shooter&nbsp;shooter; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;shooter.Shoot(rand()); </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;Audience&nbsp;audience(shooter.GetState()); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;audience.Reaction(); </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;shooter.Shoot(rand()); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;Audience&nbsp;audience2(shooter.GetState()); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;audience2.Reaction(); </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;shooter.Shoot(rand()); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;Audience&nbsp;audience3(shooter.GetState()); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;audience3.Reaction(); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;0; </span></li>    <li><span>}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>#include &quot;stdafx.h&quot;<br/>#include &lt;cstdlib&gt;<br/>#include &quot;windows.h&quot;<br/><br/>class Shooter<br/>{<br/>private:<br/>	enum{BINGO, TAT};<br/>	int state;<br/>public:<br/>	Shooter(){state = TAT; };<br/>	void Shoot(int random)<br/>	{<br/>		if(random%2 == 0){state = BINGO; printf(&quot;射手：好野！打中了！\n&quot;); }<br/>		else {state = TAT; printf(&quot;射手：5555！打不中！\n&quot;); }<br/>	}<br/>	int GetState(){return state;}<br/>};<br/><br/>class Audience<br/>{<br/>private:<br/>	enum{WOW, DAMN};<br/>	int state;<br/>public:<br/>                   Audience(int _state){ state = _state; }<br/>	void Reaction()<br/>	{<br/>		if(state == DAMN){ printf(&quot;观众：咦~~有无搞错啊，甘都打5中~~~食sh*t啦你~\n\n&quot;); }<br/>        else if(state == WOW){printf(&quot;观众：哇，打中了！好犀利啊~~~奖旧sh*t比你食喇~\n\n&quot;); }<br/>	}<br/><br/>};<br/><br/>int main(int argc, char* argv[])<br/>{<br/>	srand(GetTickCount());<br/><br/>	Shooter shooter;<br/>    shooter.Shoot(rand());<br/><br/>	Audience audience(shooter.GetState());<br/>	audience.Reaction();<br/><br/>   shooter.Shoot(rand());<br/>	Audience audience2(shooter.GetState());<br/>	audience2.Reaction();<br/><br/>   shooter.Shoot(rand());<br/>	Audience audience3(shooter.GetState());<br/>	audience3.Reaction();<br/>	<br/>	return 0;<br/>}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>注意，在main引导的三次射击中，观众1，2，3分别发表了&ldquo;评论&rdquo;，他们必须先从这个shooter的GetState()方法中得知射击结果。</p><p>然而，为了不影响射击手的情绪，比赛决定在一个对外封闭的场馆里进行（那观众还来干啥！？），观众怎么知道赛况？怎么联系两者？我忽略公告板的存在，于是我采用BOB大叔给的建议：由射击手发出结果信息，观众将实时接收信息作出反应。先看看main函数的改变：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">int</span><span>&nbsp;main(</span><span class="keyword">int</span><span>&nbsp;argc,&nbsp;</span><span class="keyword">char</span><span>*&nbsp;argv[]) </span></span></li>    <li><span>{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;srand(GetTickCount()); </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;Shooter&nbsp;shooter;</span><span class="comment">//有一个射击手 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;Audience&nbsp;audience1(1,&amp;shooter);</span><span class="comment">//3个观众想关注这个射击手 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;Audience&nbsp;audience2(2,&amp;shooter); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;Audience&nbsp;audience3(3,&amp;shooter); </span></li>    <li class="alt">&nbsp;</li>    <li><span class="comment">//设定:射击手要把结果通知这3个观众 </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;shooter.registerObserver(audience1);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;shooter.registerObserver(audience2); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;shooter.registerObserver(audience3); </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;shooter.Shoot(rand());</span><span class="comment">//射手照常射击，观众会自动获知结果 </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;shooter.Shoot(rand()); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;shooter.Shoot(rand()); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;0; </span></li>    <li><span>}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>int main(int argc, char* argv[])<br/>{<br/>	srand(GetTickCount());<br/><br/>	Shooter shooter;//有一个射击手<br/>	Audience audience1(1,&amp;shooter);//3个观众想关注这个射击手<br/>	Audience audience2(2,&amp;shooter);<br/>	Audience audience3(3,&amp;shooter);<br/><br/>//设定:射击手要把结果通知这3个观众<br/>    shooter.registerObserver(audience1);     <br/>    shooter.registerObserver(audience2);<br/>    shooter.registerObserver(audience3);<br/><br/>    shooter.Shoot(rand());//射手照常射击，观众会自动获知结果<br/>    shooter.Shoot(rand());<br/>    shooter.Shoot(rand());<br/>	<br/>	return 0;<br/>}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>你可以发现最大的不同在于关键字&ldquo;自动&rdquo;。registerObserver是为了映射这种自动通知关系的。在这里,射击手扮演了这个故事的主角&mdash;&mdash;行动表演者（performer），而三个观众扮演了旁观者，也就是&ldquo;观察者&rdquo;（observer）。因为我用的是较简单的&ldquo;拉&rdquo;模型，因此观众同样是需要获得射手的射击情况（状态）的（与此相对，&ldquo;推&rdquo;模型里射击手主动把结果告知观察者），但这次不同的是行动被内化了&mdash;&mdash;就测试代码表层来说，只要射击手&ldquo;同意&rdquo;把该观众作为自己的&ldquo;通知&rdquo;对象，该观众就有资格获得射击手射击成败的信息。接下来射击手只管射击和自我咏叹（Shoot方法），而观众&ldquo;自动&rdquo;作出反应。</p><p>首先，抽象出双方的接口，然后，Subject类维护了一个名单，也就是针对某射击手的观察者名单，具有注册和通知功能（这是最基本的，可自加其他功能，譬如移除等），registerObserver把一个新的观察者引用加入通知列表，notifyObserver() 引起各个注册者的行动。</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">class</span><span>&nbsp;Performer </span></span></li>    <li><span>{ </span></li>    <li class="alt"><span class="keyword">public</span><span>: </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">virtual</span><span>&nbsp;~Performer(){}; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">virtual</span><span>&nbsp;</span><span class="keyword">int</span><span>&nbsp;GetState()&nbsp;=&nbsp;0; </span></li>    <li><span>}; </span></li>    <li class="alt">&nbsp;</li>    <li><span class="keyword">class</span><span>&nbsp;Observer </span></li>    <li class="alt"><span>{ </span></li>    <li><span class="keyword">public</span><span>: </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">virtual</span><span>&nbsp;~Observer(){}; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">virtual</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;Reaction()&nbsp;=&nbsp;0;&nbsp;&nbsp;&nbsp;&nbsp; </span></li>    <li class="alt"><span>}; </span></li>    <li>&nbsp;</li>    <li class="alt"><span class="keyword">class</span><span>&nbsp;Subject </span></li>    <li><span>{ </span></li>    <li class="alt"><span class="keyword">private</span><span>: </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;std::vector&lt;Observer*&gt;&nbsp;ObserverVessel; </span></li>    <li class="alt"><span class="keyword">public</span><span>: </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">virtual</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;notifyObserver() </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>(std::vector&lt;Observer*&gt;::iterator&nbsp;p&nbsp;=&nbsp;ObserverVessel.begin();&nbsp;p!=&nbsp;ObserverVessel.end();&nbsp;p++) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(*p)-&gt;Reaction(); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">virtual</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;registerObserver(Observer&nbsp;&amp;newobsever) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ObserverVessel.push_back(&amp;newobsever); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li>&nbsp;</li>    <li class="alt"><span>};</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>class Performer<br/>{<br/>public:<br/>	virtual ~Performer(){};<br/>	virtual int GetState() = 0;<br/>};<br/><br/>class Observer<br/>{<br/>public:<br/>	virtual ~Observer(){};<br/>	virtual void Reaction() = 0;	<br/>};<br/><br/>class Subject<br/>{<br/>private:<br/>	std::vector&lt;Observer*&gt; ObserverVessel;<br/>public:<br/>	virtual void notifyObserver()<br/>	{<br/>		for(std::vector&lt;Observer*&gt;::iterator p = ObserverVessel.begin(); p!= ObserverVessel.end(); p++)<br/>			(*p)-&gt;Reaction();<br/>	}<br/><br/>	virtual void registerObserver(Observer &amp;newobsever)<br/>	{<br/>      ObserverVessel.push_back(&amp;newobsever);<br/>	}<br/><br/>};</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>显然，名单归射击手所有，因此接下来Shooter要扮演这个Subject的角色，同时也作为行动者。因为BOB大叔的那代码是JAVA写的，我不知道C++怎么处理JAVA和C#里那种&ldquo;既继承某接口又implement某类&rdquo;的表达方式，能想到的只有多继承了，抱歉。注意，简单起见，我仅规定Audience类接受单一关注者（myperformer），并把该被关注者（也就是射手）的射击状态关联观众的状态（&ldquo;拉&rdquo;模型）：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="keyword">class</span><span>&nbsp;Shooter&nbsp;:&nbsp;</span><span class="keyword">public</span><span>&nbsp;Performer,&nbsp;</span><span class="keyword">public</span><span>&nbsp;Subject </span></span></li>    <li><span>{ </span></li>    <li class="alt"><span class="keyword">private</span><span>: </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">enum</span><span>{BINGO,&nbsp;TAT}; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">int</span><span>&nbsp;state; </span></li>    <li><span class="keyword">public</span><span>: </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;Shooter(){state&nbsp;=&nbsp;TAT;&nbsp;}; </span></li>    <li>&nbsp;</li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">void</span><span>&nbsp;Shoot(</span><span class="keyword">int</span><span>&nbsp;random) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(random%2&nbsp;==&nbsp;0){state&nbsp;=&nbsp;BINGO;&nbsp;printf(</span><span class="string">&quot;\n射手：好野！打中了！\n&quot;</span><span>);&nbsp;} </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span><span>&nbsp;</span><span class="keyword">if</span><span>(random%2&nbsp;==&nbsp;1){state&nbsp;=&nbsp;TAT;&nbsp;printf(</span><span class="string">&quot;\n射手：5555！打不中！\n&quot;</span><span>);&nbsp;} </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;notifyObserver(); </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">virtual</span><span>&nbsp;</span><span class="keyword">int</span><span>&nbsp;GetState(){</span><span class="keyword">return</span><span>&nbsp;state;} </span></li>    <li><span>}; </span></li>    <li class="alt">&nbsp;</li>    <li><span class="keyword">class</span><span>&nbsp;Audience&nbsp;:&nbsp;</span><span class="keyword">public</span><span>&nbsp;Observer </span></li>    <li class="alt"><span>{ </span></li>    <li><span class="keyword">private</span><span>: </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">enum</span><span>{WOW,&nbsp;DAMN}; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;Performer&nbsp;*myperformer; </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">int</span><span>&nbsp;audince_ID; </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">int</span><span>&nbsp;state; </span></li>    <li class="alt"><span class="keyword">public</span><span>: </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;Audience(</span><span class="keyword">int</span><span>&nbsp;audince_id,&nbsp;Performer&nbsp;*_performer) </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:audince_ID(audince_id),&nbsp;myperformer(_performer) </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;{} </span></li>    <li class="alt">&nbsp;</li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">virtual</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;Reaction() </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;{ </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;state&nbsp;=&nbsp;myperformer-&gt;GetState(); </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>(state&nbsp;==&nbsp;DAMN){&nbsp;printf(</span><span class="string">&quot;观众%d：咦~~有无搞错啊，甘都打5中~~~食sh*t啦你~\n&quot;</span><span>,&nbsp;audince_ID);&nbsp;} </span></li>    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">else</span><span>&nbsp;</span><span class="keyword">if</span><span>(state&nbsp;==&nbsp;WOW){printf(</span><span class="string">&quot;观众%d：哇，打中了！好犀利啊~~~奖旧sh*t比你食喇~\n&quot;</span><span>,&nbsp;audince_ID);&nbsp;} </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;} </span></li>    <li><span>};</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>class Shooter : public Performer, public Subject<br/>{<br/>private:<br/>	enum{BINGO, TAT};<br/>	int state;<br/>public:<br/>	Shooter(){state = TAT; };<br/><br/>	void Shoot(int random)<br/>	{<br/>		if(random%2 == 0){state = BINGO; printf(&quot;\n射手：好野！打中了！\n&quot;); }<br/>		else if(random%2 == 1){state = TAT; printf(&quot;\n射手：5555！打不中！\n&quot;); }<br/>		notifyObserver();<br/>	}<br/>	virtual int GetState(){return state;}<br/>};<br/><br/>class Audience : public Observer<br/>{<br/>private:<br/>	enum{WOW, DAMN};<br/>	Performer *myperformer;<br/>    int audince_ID;<br/>	int state;<br/>public:<br/>    Audience(int audince_id, Performer *_performer)<br/>		:audince_ID(audince_id), myperformer(_performer)<br/>	{}<br/><br/>	virtual void Reaction()<br/>	{<br/>		state = myperformer-&gt;GetState();<br/>		if(state == DAMN){ printf(&quot;观众%d：咦~~有无搞错啊，甘都打5中~~~食sh*t啦你~\n&quot;, audince_ID); }<br/>        else if(state == WOW){printf(&quot;观众%d：哇，打中了！好犀利啊~~~奖旧sh*t比你食喇~\n&quot;, audince_ID); }<br/>	}<br/>};</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>当然，以上只是设计模式之观察者(Observer)的最简单应用，但希望能让看官稍微感受它的思想。恩，它不是公式，而是软件设计思想&mdash;&mdash;这种思想我谓之&ldquo;伺机而动&rdquo;。</p><p>以上歪主意的代码：<a target="_blank" href="http://www.zwqxin.com/upload/2009/3/ObserverTest.rar">ObserverTest.rar</a></p>]]></description><category>C/C++</category><comments>http://www.zwqxin.com/archives/cpp/learn-design-pattern-obsever.html#comment</comments><wfw:comment>http://www.zwqxin.com/</wfw:comment><wfw:commentRss>http://www.zwqxin.com/feed.asp?cmt=35</wfw:commentRss><trackback:ping>http://www.zwqxin.com/cmd.asp?act=tb&amp;id=35&amp;key=0a5312b5</trackback:ping></item><item><title>成员函数指针使用小感</title><author>a@b.com (zwqxin)</author><link>http://www.zwqxin.com/archives/cpp/use-member-function-pointer.html</link><pubDate>Fri, 27 Feb 2009 18:28:47 +0800</pubDate><guid>http://www.zwqxin.com/archives/cpp/use-member-function-pointer.html</guid><description><![CDATA[<p>&nbsp;今天整理代码的时候，早有预感地，要遇上成员函数指针的使用了。其实自从C语言开始，我对成员函数指针这东西还停留一知半解的阶段，惭愧ING。还好，今天对它的使用&ldquo;手法&rdquo;又多了点了解。使用成员函数指针使用小感&mdash;&mdash;<a href="http://www.zwqxin.com/">ZwqXin</a>.com</p><p>事情是这样的。我今天把CSM(<strong>Cascading Shadow Maps</strong>)的结构清理一下，虽然说只是单单把涉及CSM算法的代码抽出CMainFrame并不是什么真正的重构，但起码别让一堆代码堆挤在一起吧。一切单纯而懒散地进行着，终于来到了最后一个编译错误（最后一个要改的地方）：</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="comment">//在CMainFrame中： </span></span></li>    <li><span class="comment">//.... </span></li>    <li class="alt"><span>CCascadingSM&nbsp;CascadingSM;</span><span class="comment">//CSM对象 </span></li>    <li><span class="comment">//..... </span></li>    <li class="alt"><span class="keyword">void</span><span>&nbsp;CMainFrame::RenderObjects() </span></li>    <li><span>{.....} </span></li>    <li class="alt">&nbsp;</li>    <li><span class="keyword">void</span><span>&nbsp;CMainFrame::RenderGLScene() </span></li>    <li class="alt"><span>{ </span></li>    <li><span>..... </span></li>    <li class="alt"><span>CascadingSM.GenShadowMap(....); </span></li>    <li><span>...... </span></li>    <li class="alt"><span>CascadingSM.CastShadowMap(....); </span></li>    <li><span>} </span></li>    <li class="alt"><span class="comment">//-------------------------类间分割线------------- </span></li>    <li><span class="comment">//在CCascadingSM中： </span></li>    <li class="alt"><span class="keyword">void</span><span>&nbsp;CCascadingSM::GenShadowMap(......) </span></li>    <li><span>{ </span></li>    <li class="alt"><span>.... </span></li>    <li><span>（）&nbsp;&nbsp;</span><span class="comment">//-----本来放RenderObjects()的地方 </span></li>    <li class="alt"><span>.... </span></li>    <li><span>} </span></li>    <li class="alt"><span class="keyword">void</span><span>&nbsp;CCascadingSM::CastShadowMap(......) </span></li>    <li><span>{ </span></li>    <li class="alt"><span>.... </span></li>    <li><span>（）&nbsp;&nbsp;</span><span class="comment">//-----本来放RenderObjects()的地方 </span></li>    <li class="alt"><span>.... </span></li>    <li><span>}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>//在CMainFrame中：<br/>//....<br/>CCascadingSM CascadingSM;//CSM对象<br/>//.....<br/>void CMainFrame::RenderObjects()<br/>{.....}<br/><br/>void CMainFrame::RenderGLScene()<br/>{<br/>.....<br/>CascadingSM.GenShadowMap(....);<br/>......<br/>CascadingSM.CastShadowMap(....);<br/>}<br/>//-------------------------类间分割线-------------<br/>//在CCascadingSM中：<br/>void CCascadingSM::GenShadowMap(......)<br/>{<br/>....<br/>（）  //-----本来放RenderObjects()的地方<br/>....<br/>}<br/>void CCascadingSM::CastShadowMap(......)<br/>{<br/>....<br/>（）  //-----本来放RenderObjects()的地方<br/>....<br/>}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>熟悉<a target="_blank" href="http://www.zwqxin.com/archives/opengl/shadow-map-1.html">Shadow Map</a>的朋友都知道，GenShadowMap和CastShadowMap就是Shadow Map算法的两步走（两个PASS），分别是光源视觉下对场景生成阴影贴图（深度图），以及把该深度图应用于正常场景中做深度比较以生成阴影。两个函数都需要对场景（也就是RenderObjects()）进行渲染。但是场景RenderObjects不属于CSM算法的一部分，所以我不打算把它交给CCascadingSM类。再者它不大，所以我也不打算让它单独成类，还是把它留在CMainFrame好了。</p><p>我看网上一些图形学教程，都喜欢过程式设计，对抽象封装不怎么在意（也许是侧重点不同吧，它们的目的在于呈现图形学API使用，算法之类。但其实我一直觉得单纯图形学作品设计，用过程式思维是最快捷方便的），他们多把东西放一个CPP里搞掂。其实我今天没进行代码整理之前也就这样子了。不过也因此对C++类封装设计不怎么敏感，至于成员函数指针这东西.......</p><p>好吧，请原谅我，我以前的处理方法就是把要调用成员函数指针的类，整个static静态化......这样当然不太好，就为了一个成员函数指针把整个类，有辜无辜的都弄变态了。但是当时年少无知的我真的google不出其他解决之道了（现在看来是关键字不对？），乐呵呵地编译成功就继续其他的了。....当然这是个办法，但是这次我忧郁了，因为我面对的是直接的应用类CMainFrame，好吧，我是不敢这样做了。局部静态化呢？不懂，google出来，也不太想看。boost的mem_fun?当时用一次就头疼（VC6还得boost来除BUG呀）。还有一个方法，可以类成员函数里可以保留其他该类的变量啊函数，而且不怕继承那一套的：给个全局的辅助函数吧。</p><p>这里有些资料是关于这些方法的：<a target="_blank" href="http://www.yesky.com/207/1844207.shtml">C++指针直接调用类成员函数探讨</a>&nbsp;，我正想用它的第3个解决之道，但它里面介绍是同类间调用的，好吧，变通一下就可以了。</p><div class="HighLighter" contenteditable="false"><div class="dp-highlighter" contenteditable="false"><div class="bar">&nbsp;</div><ol class="dp-c">    <li class="alt"><span><span class="comment">//在CMainFrame中： </span></span></li>    <li><span class="comment">//.... </span></li>    <li class="alt"><span>CCascadingSM&nbsp;CascadingSM;</span><span class="comment">//CSM对象 </span></li>    <li><span class="comment">//..... </span></li>    <li class="alt"><span class="keyword">void</span><span>&nbsp;CMainFrame::RenderObjects() </span></li>    <li><span>{.....} </span></li>    <li class="alt">&nbsp;</li>    <li><span class="comment">//重点：帮拖的全局辅助函数： </span></li>    <li class="alt"><span class="keyword">void</span><span>&nbsp;Help_RenderObjects(CMainFrame&nbsp;*mf) </span></li>    <li><span>{ </span></li>    <li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;mf-&gt;RenderObjects(); </span></li>    <li><span>} </span></li>    <li class="alt"><span class="comment">////// </span></li>    <li>&nbsp;</li>    <li class="alt"><span class="keyword">void</span><span>&nbsp;CMainFrame::RenderGLScene() </span></li>    <li><span>{ </span></li>    <li class="alt"><span>....... </span></li>    <li><span>CascadingSM.GenShadowMap(</span><span class="keyword">this</span><span>,&nbsp;Help_RenderObjects....); </span></li>    <li class="alt"><span>....... </span></li>    <li><span>CascadingSM.CastShadowMap(</span><span class="keyword">this</span><span>,&nbsp;Help_RenderObjects); </span></li>    <li class="alt"><span>} </span></li>    <li><span class="comment">//-------------------------类间分割线------------- </span></li>    <li class="alt"><span class="comment">//在CCascadingSM类中： </span></li>    <li><span class="keyword">class</span><span>&nbsp;CMainFrame;</span><span class="comment">//前置声明，写在类声明前头 </span></li>    <li class="alt"><span>..... </span></li>    <li><span class="keyword">void</span><span>&nbsp;CCascadingSM::GenShadowMap(CMainFrame&nbsp;*Mf,</span><span class="keyword">void</span><span>&nbsp;(*scene)(CMainFrame&nbsp;*mf)...) </span></li>    <li class="alt"><span>{ </span></li>    <li><span>.... </span></li>    <li class="alt"><span>(*scene)(Mf);&nbsp;&nbsp;</span><span class="comment">//-----本来放RenderObjects()的地方 </span></li>    <li><span>.... </span></li>    <li class="alt"><span>} </span></li>    <li><span class="keyword">void</span><span>&nbsp;CCascadingSM::CastShadowMap(CMainFrame&nbsp;*Mf,</span><span class="keyword">void</span><span>&nbsp;(*scene)(CMainFrame&nbsp;*mf)) </span></li>    <li class="alt"><span>{ </span></li>    <li><span>.... </span></li>    <li class="alt"><span>(*scene)(Mf);&nbsp;&nbsp;</span><span class="comment">//-----本来放RenderObjects()的地方 </span></li>    <li><span>.... </span></li>    <li class="alt"><span>}</span></li></ol></div><div class="c#" contenteditable="false" style="display: none"><pre>//在CMainFrame中：<br/>//....<br/>CCascadingSM CascadingSM;//CSM对象<br/>//.....<br/>void CMainFrame::RenderObjects()<br/>{.....}<br/><br/>//重点：帮拖的全局辅助函数：<br/>void Help_RenderObjects(CMainFrame *mf)<br/>{<br/>    mf-&gt;RenderObjects();<br/>}<br/>//////<br/><br/>void CMainFrame::RenderGLScene()<br/>{<br/>.......<br/>CascadingSM.GenShadowMap(this, Help_RenderObjects....);<br/>.......<br/>CascadingSM.CastShadowMap(this, Help_RenderObjects);<br/>}<br/>//-------------------------类间分割线-------------<br/>//在CCascadingSM类中：<br/>class CMainFrame;//前置声明，写在类声明前头<br/>.....<br/>void CCascadingSM::GenShadowMap(CMainFrame *Mf,void (*scene)(CMainFrame *mf)...)<br/>{<br/>....<br/>(*scene)(Mf);  //-----本来放RenderObjects()的地方<br/>....<br/>}<br/>void CCascadingSM::CastShadowMap(CMainFrame *Mf,void (*scene)(CMainFrame *mf))<br/>{<br/>....<br/>(*scene)(Mf);  //-----本来放RenderObjects()的地方<br/>....<br/>}</pre></div><div contenteditable="false"><link href="/admin/FCKeditor/editor/plugins/highlighter/dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" type="text/css" rel="stylesheet" /></div></div><p>先看CCascadingSM类，一般的函数指针调用是<span class="keyword">void</span><span>&nbsp;(*scene)()这样的吧，这里给它一个参数CMainFrame&nbsp;*mf，因此GenShadowMap函数参数列表要再加个CMainFrame&nbsp;*Mf， 这样调用的时候就可以写<span>(*scene)(Mf);了（该函数指针有多少个参数，就在GenShadowMap函数参数列表里再加多少个对应的变量）。好吧，这里的指针相当于回调函数，回调一个void类型的以CMainFrame对象为参的函数。</span></span></p><p><span><span>我们目的是要让它回调另一个类&mdash;&mdash;CmainFrame的RenderObjects()，这就是成员函数指针调用。然而，直接来是不可以的，因为众所周知，你要获得另一个类里的函数的指针，起码得知道该类生成的某个具体对象的地址。只有具体的东西才有指针而言。那么&mdash;&mdash;我们是要创建一个CmainFrame对象么？不行，一来建这种应用类的对象可是大工程啊，二来就是你建了，那里面的RenderObjects()也不是你要的RenderObjects()了&mdash;&mdash;我要的是在OPENGL窗口里持续运行的CmainFrame类，获得它的办法，就是this指针。</span></span></p><p><span><span>Help_RenderObjects(CMainFrame&nbsp;*mf)是连接一切的小桥梁。它需要的正是一个CMainFrame类型的指针，出来的结果就是这个指针下的成员函数RenderObjects()。明朗起来了，给它this指针，它就给我我所需要的那个RenderObjects()。因此，CCascadingSM类的GenShadowMap函数回调它（参数:this,GenShadowMap）就得到RenderObjects()，注意全局函数或static化的函数是不需要具体对象的支持的。一切完好。</span></span></p><p><span><span>恩，现在我最关心的是：效率如何？也许跟它无关，但是，也不知道调整代码过程中哪里给我出的桩，FPS降低了！！</span></span></p>]]></description><category>C/C++</category><comments>http://www.zwqxin.com/archives/cpp/use-member-function-pointer.html#comment</comments><wfw:comment>http://www.zwqxin.com/</wfw:comment><wfw:commentRss>http://www.zwqxin.com/feed.asp?cmt=33</wfw:commentRss><trackback:ping>http://www.zwqxin.com/cmd.asp?act=tb&amp;id=33&amp;key=5c35b4e1</trackback:ping></item></channel></rss>
