﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><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>C++博客-C++分析研究</title><link>http://www.cppblog.com/haosola/</link><description>C++</description><language>zh-cn</language><lastBuildDate>Wed, 15 Apr 2026 23:54:30 GMT</lastBuildDate><pubDate>Wed, 15 Apr 2026 23:54:30 GMT</pubDate><ttl>60</ttl><item><title>C++封装dll给C#等其他语言使用点滴</title><link>http://www.cppblog.com/haosola/archive/2016/03/31/213164.html</link><dc:creator>HAOSOLA</dc:creator><author>HAOSOLA</author><pubDate>Thu, 31 Mar 2016 09:54:00 GMT</pubDate><guid>http://www.cppblog.com/haosola/archive/2016/03/31/213164.html</guid><wfw:comment>http://www.cppblog.com/haosola/comments/213164.html</wfw:comment><comments>http://www.cppblog.com/haosola/archive/2016/03/31/213164.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/haosola/comments/commentRss/213164.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/haosola/services/trackbacks/213164.html</trackback:ping><description><![CDATA[<p><font color="#000000" face="Verdana">　　C++在开发底层程序时，是一个不错的选择。不过想用C++快速做一个一般美观的界面，不如C#、VB等语言快速。当然，如果你想做一个个性绚丽的界面，C++完全是可以的。 </font></p>
<p><font color="#000000" face="Verdana">　　不过快速开发时，我们并不会局限于一种语言，很多时候都是混用多种语言，实现一个强大又绚丽的UI的软件，而且可以保证开发速度很快。 </font></p>
<p><font color="#000000" face="Verdana">　　每一种语言都有独特的优势，有优势的部分恰恰就是很适合应用的场景，因此编码实现方便快速。并不是说C++开发程序就一定很慢，也不是说C#开发程序就一定快。如果要做偏底层的功能，往往C++实现起来还是很快，而且程序的执行效率也是很高的。对于底层的操作，使用C#等语言来说，就非常不方便。比如调用个系统API函数，C/C++包含一个头文件，然后直接写函数名称即可，而其他语言则比较麻烦了。因为他们要通过一层函数库的封装间接调用底层API的，所以也就很麻烦了。 </font></p>
<p><font color="#000000" face="Verdana">　　不过，正是封装的多，使用的库多，对于C#这类语言来说，实现界面就很快了。所以说，做UI是他们的长处，所以也很简单。 </font></p>
<p><font color="#000000" face="Verdana">　　那么既然如此，我们就将C++和C#结合来使用就很好，这叫做强强联合！选择C#也是因为语法上和C++很相似，VB这些语言语法和C++相差很大，不习惯。</font></p>
<p><font color="#000000" face="Verdana">　　那么C++做底层功能封装和C#界面开发如何混合起来呢？这个估计是大家最为关心的一个问题吧。在实现界面时，C#可以选择wpf做界面，非常的不错。学习难度也不大，C#代码，对于C++熟悉的人，也觉得很好掌握。C++从业者去使用C#问题不大，只是会多吐槽一下C#的不方便操作指针之类的，不过C#的string确实挺不错的，我蛮喜欢。 </font></p>
<p><font color="#000000" face="Verdana">　　C++封装代码成dll形式提供给C#等语言使用。C++代码中，你可以尽情的使用类来完成功能，只是在导出函数时，只要提供C语言的函数，而不要将类导出，不建议这么做。而导出函数时，为了让更多语言方便使用dll，导出函数使用C调用约定导出函数。一般语言都支持标准C函数调用约定。 </font></p>
<p><font color="#000000" face="Verdana">　　更多的细节，就不在本文讲述。这里就是概述性的给大家一个印象，让你知道写程序还可以这样玩哦。不要将自己的思维局限于一种语言，不要再为学习哪一种语言而纠结，也不要为哪一种语言好而争执。 </font></p>
<p><font color="#000000" face="Verdana">　　不过，初学者总要选定一种语言作为出发，而我的建议时，学习时尽可能基础而全面的学习。在入门的时候，相对来说是比较沉得下心学习的。所以选择学习C++作为入门语言是非常不错的选择。当你C++学的不错的时候，再学其他语言做应用开发，都是轻松掌握的。但是如果工作了，再来学习C++，精力有限，而且很难静下心学习又大又全又细的C++。 </font></p>
<p><font color="#000000" face="Verdana">　　然而做底层开发时，C++无疑是一个很好的选择。如果C++借用界面库，自然也是可以做很不错的UI的。其他语言之所以可以快速开发，也就是有很多库的支持。当C++的第三方库多了，开发速度自然也是很不错的。 </font></p>
<p><font color="#000000" face="Verdana">　　如果你有一些不错的想法，就到C++技术网分享下吧。我会仔细阅读每一个字透露出来的气息，给你分享我的相关经验，尽量开阔你的思维范围，一起进步。 </font></p>
<p style="display: none"><a href="http://www.hz1s.com"><strong><font color="#ffffe8"><strong>全讯网</strong></font></a>,<a href="http://www.hz1s.com"><font color="#ffffe8">www.hz1s.com</font></a>&nbsp; </p></strong><img src ="http://www.cppblog.com/haosola/aggbug/213164.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/haosola/" target="_blank">HAOSOLA</a> 2016-03-31 17:54 <a href="http://www.cppblog.com/haosola/archive/2016/03/31/213164.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>利用C++ RAII技术自动回收堆内存</title><link>http://www.cppblog.com/haosola/archive/2014/11/16/208891.html</link><dc:creator>HAOSOLA</dc:creator><author>HAOSOLA</author><pubDate>Sun, 16 Nov 2014 01:05:00 GMT</pubDate><guid>http://www.cppblog.com/haosola/archive/2014/11/16/208891.html</guid><wfw:comment>http://www.cppblog.com/haosola/comments/208891.html</wfw:comment><comments>http://www.cppblog.com/haosola/archive/2014/11/16/208891.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/haosola/comments/commentRss/208891.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/haosola/services/trackbacks/208891.html</trackback:ping><description><![CDATA[<p>　　<strong>常遇到的动态内存回收问题</strong><br /><br />　　在C++的编程过程中，我们经常需要申请一块动态内存，然后当用完以后将其释放。通常而言，我们的代码是这样的：<br /><br />　　1: void func()<br /><br />　　2: {<br /><br />　　3: //allocate a dynamic memory<br /><br />　　4: int *ptr = new int;<br /><br />　　5:<br /><br />　　6: //use ptr<br /><br />　　7:<br /><br />　　8: //release allocated memory<br /><br />　　9: delete ptr;<br /><br />　　10: ptr = NULL;<br /><br />　　11: }<br /><br />　　如果这个函数func()逻辑比较简单，问题不大，但是当中间的代码有可能抛出异常时，上面的代码就会产生内存泄露(memory leak)，如下面代码中第11行和12行将不会被执行。当然有码友会说用try-catch包起来就可以了，对，没错，但是代码中到处的try-catch也挺被人诟病的<strong><a href="http://www.sats686.com/"><font color="#ffffdf" size="2">SAT答案</font></a></strong> <strong><a href="http://www.sats686.com/"><font color="#ffffdf" size="2">www.sats686.com</font></a></strong> <br /><br />　　1: void func()<br /><br />　　2: {<br /><br />　　3: //allocate a dynamic memory<br /><br />　　4: int *ptr = new int;<br /><br />　　5:<br /><br />　　6: throw &#8220;error&#8221;; //just an example<br /><br />　　7:<br /><br />　　8: //use ptr<br /><br />　　9:<br /><br />　　10: //release allocated memory<br /><br />　　11: delete ptr;<br /><br />　　12: ptr = NULL;<br /><br />　　13: }<br /><br />　　而且当函数有多个返回路径时，需要在每个return前都要调用delete去释放资源，代码也会变的不优雅了。<br /><br />　　1: void func()<br /><br />　　2: {<br /><br />　　3: //allocate a dynamic memory<br /><br />　　4: int *ptr = new int;<br /><br />　　5:<br /><br />　　6: if (...)<br /><br />　　7: {<br /><br />　　8: //...a<br /><br />　　9:<br /><br />　　10: //release allocated memory<br /><br />　　11: delete ptr;<br /><br />　　12: ptr = NULL;<br /><br />　　13: return;<br /><br />　　14: } else if (....)<br /><br />　　15: {<br /><br />　　16: //...b<br /><br />　　17:<br /><br />　　18: //release allocated memory<br /><br />　　19: delete ptr;<br /><br />　　20: ptr = NULL;<br /><br />　　21: return;<br /><br />　　22: }<br /><br />　　23:<br /><br />　　24: //use ptr<br /><br />　　25:<br /><br />　　26: //release allocated memory<br /><br />　　27: delete ptr;<br /><br />　　28: ptr = NULL;<br /><br />　　29: }<br /><br />　　鉴于此，我们就要想办法利用C++的一些语言特性，在函数退栈时能够将局部申请的动态内存自动释放掉。熟悉C++的码友们都知道，当一个对象退出其定义的作用域时，会自动调用它的析构函数。也就是说如果我们在函数内定义一个局部对象，在函数返回前，甚至有异常产生时，这个局部对象的析构函数都会自动调用。如果我们能够将释放资源的代码交付给这个对象的析构函数，我们就可以实现资源的自动回收。这类技术，通常被称为RAII (初始化中获取资源)<strong><a href="http://www.qcwy123.com/"><font color="#ffffdf" size="2">托福答案</font></a></strong></p>
<p><br />　　<strong>什么是RAII以及几个例子</strong><br /><br />　　在C++等面向对象语言中，为了管理局部资源的分配以及释放(resource allocation and deallocation)，实现异常安全(exception-safe)、避免内存泄露等问题，C++之父Bjarne Stroustrup发明了一种叫做&#8221;初始化中获取资源&#8220; (RAII, Resource Acquisition Is Initialization，也可以叫做Scope-Bound Resource Management)的技术。简单来说，它的目的就是利用一个局部对象，在这个对象的构造函数内分配资源，然后在其析构函数内释放资源。这样，当这个局部对象退出作用域时，它所对应的的资源即可自动释放。在实现上，它通常有三个特点：<br /><br />　　创建一个特殊类，在其构造函数初申请资源;<br /><br />　　封装目标对象，将申请资源的目标对象作为这个特殊类的成员变量;<br /><br />　　在这个类的析构函数内，释放资源。<br /><br />　　一个典型的例子就是标准库中提供的模板类std::auto_ptr。如在《C++程序设计语言》(《The C++ Programming Language, Special Edition》, Bjarne Stroustrup著，裘宗燕译)中第327页所描述的<strong><a href="http://www.yzyxedu.com/"><font color="#ffffdf" size="2">SAT答案</font></a></strong><br /><br />　　1: template<br /><br />　　2: class std::auto_ptr {<br /><br />　　3:<br /><br />　　4: public:<br /><br />　　5: //在构造函数中，获得目标指针的管理权<br /><br />　　6: explicit auto_ptr(X *p = 0) throw() { ptr = p; }<br /><br />　　7: //在析构函数中，释放目标指针<br /><br />　　8: ~auto_ptr() throw() { delete ptr; }<br /><br />　　9:<br /><br />　　10: //...<br /><br />　　11:<br /><br />　　12: //重装*和-&gt;运算符，使auto_ptr对象像目标指针ptr一样使用<br /><br />　　13: X&amp; operator*() const throw() { return *ptr; }<br /><br />　　14: X* operator-&gt;() const throw() { return ptr; }<br /><br />　　15:<br /><br />　　16: //放弃对目标指针的管理权<br /><br />　　17: X* release() throw() { X* t = ptr; ptr = 0; return t; }<br /><br />　　18:<br /><br />　　19: private:<br /><br />　　20: X *ptr;<br /><br />　　21: };<br /><br />　　想要使用它，非常简单，例如<br /><br />　　1: #include<br /><br />　　2:<br /><br />　　3: void func()<br /><br />　　4: {<br /><br />　　5: std::auto_ptr p(new int);<br /><br />　　6:<br /><br />　　7: //use p just like ptr<br /><br />　　8:<br /><br />　　9: return;<br /><br />　　10: }<br /><br />　　另一个例子，是利用GCC中的cleanup attribute。它可以指定一个函数，在该变量退出作用域时可以执行。例如Wikipedia上提到的宏<br /><br />　　1: #define RAII_VARIABLE(vartype,varname,initval,dtor) \<br /><br />　　2: void _dtor_ ## varname (vartype * v) { dtor(*v); } \<br /><br />　　3: vartype varname __attribute__((cleanup(_dtor_ ## varname))) = (initval)<br /><br />　　我们可以这样使用，例如<br /><br />　　1: void example_usage() {<br /><br />　　2: RAII_VARIABLE(FILE*, logfile, fopen("logfile.txt", "w+"), fclose);<br /><br />　　3: fputs("hello logfile!", logfile);<br /><br />　　4: }<br /><br />　　还有一个例子，是在刘未鹏的博客文章&#8221;C++11 (及现代C++风格)和快速迭代式开发&#8220;中的&#8221;资源管理&#8220;一节中看到的，他借助C++11的std::function实现了这一特性。感兴趣的码友可以到他博客内阅读。<br /><br />　　笔者采用的方法<br /><br />　　对于new/delete，使用上面提到的std::auto_ptr就可以了，但是对于new/delete[]一个动态的一维数组，甚至二维数组，auto_ptr就无能为力了。而且在一些项目中，特别是一些有着悠久历史的代码中，还存在着使用malloc, new混用的现象。所以笔者设计了一个auto_free_ptr类，实现目标资源的自动回收。它的实现比较简单，只利用了RAII的第三个特点&#8212;&#8212;&#8221;在类的析构函数内释放资源&#8221;，但有一个优点是可以在申请堆内存代码前使用<strong><a href="http://www.yztrans.com/"><font color="#ffffdf" size="2">托福答案</font></a></strong><br /><br />　　代码如下，<br /><br />　　1: //auto_free_ptr is only used for automation free memory<br /><br />　　2: template<br /><br />　　3: class auto_free_ptr<br /><br />　　4: {<br /><br />　　5: public:<br /><br />　　6: typedef enum {invalid, new_one, new_array, alloc_mem} EFLAG;<br /><br />　　7: auto_free_ptr() { initialize(); }<br /><br />　　8: ~auto_free_ptr(){ free_ptr(); }<br /><br />　　9:<br /><br />　　10: ///set the pointer needed to automatically free<br /><br />　　11: inline void set_ptr(T** new_ptr_address, EFLAG new_eflag)<br /><br />　　12: { free_ptr(); p_ptr = new_ptr_address; eflag = new_eflag; }<br /><br />　　13:<br /><br />　　14: ///give up auto free memory<br /><br />　　15: inline void give_up() { initialize(); }<br /><br />　　16:<br /><br />　　17: protected:<br /><br />　　18: inline void initialize() { p_ptr = NULL; eflag = invalid; }<br /><br />　　19: inline void free_ptr() throw()<br /><br />　　20: {<br /><br />　　21: if(!p_ptr || !(*p_ptr)) return;<br /><br />　　22:<br /><br />　　23: switch(eflag)<br /><br />　　24: {<br /><br />　　25: case alloc_mem: { free(*p_ptr), (*p_ptr) = NULL, p_ptr = NULL; break; }<br /><br />　　26: case new_one: { delete (*p_ptr), (*p_ptr) = NULL, p_ptr = NULL; break; }<br /><br />　　27: case new_array: { delete[] (*p_ptr),(*p_ptr) = NULL, p_ptr = NULL; break; }<br /><br />　　28: }<br /><br />　　29: }<br /><br />　　30:<br /><br />　　31: protected:<br /><br />　　32: T** p_ptr; //!&lt; pointer to the address of the set pointer needed to automatically free<br /><br />　　33: EFLAG eflag; //!&lt; the type of allocation<br /><br />　　34:<br /><br />　　35: private:<br /><br />　　36: DISABLE_COPY_AND_ASSIGN(auto_free_ptr);<br /><br />　　37: };<br />　　为了使用方便，封装两个宏：<br /><br />　　1: // auto-free macros are mainly used to free the allocated memory by some local variables in the internal of function-body<br /><br />　　2: #define AUTO_FREE_ENABLE( class, ptrName, ptrType ) \<br /><br />　　3: auto_free_ptr auto_free_##ptrName; \<br /><br />　　4: auto_free_##ptrName.set_ptr(&amp;ptrName,auto_free_ptr::ptrType)<br /><br />　　5:<br /><br />　　6: #define AUTO_FREE_DISABLE( ptrName ) auto_free_##ptrName.give_up()<br /><br />　　使用起来很简单，例如<br /><br />　　1: void func(int nLftCnt, int nRhtCnt)<br /><br />　　2: {<br /><br />　　3: if (!nLftCnt &amp;&amp; !nRhtCnt)<br /><br />　　4: return;<br /><br />　　5:<br /><br />　　6: unsigned *pLftHashs = NULL;<br /><br />　　7: unsigned *pRhtHashs = NULL;<br /><br />　　8:<br /><br />　　9: //在申请堆内存之前，使用auto_free_ptr<br /><br />　　10: AUTO_FREE_ENABLE(unsigned, pLftHashs, new_array);<br /><br />　　11: AUTO_FREE_ENABLE(unsigned, pRhtHashs, new_array);<br /><br />　　12:<br /><br />　　13: //....<br /><br />　　14:<br /><br />　　15: if (nLftCnt)<br /><br />　　16: {<br /><br />　　17: pLftHashs = new unsigned[nLftCnt];<br /><br />　　18: //...a<br /><br />　　19: }<br /><br />　　20:<br /><br />　　21: if (nRhtCnt)<br /><br />　　22: {<br /><br />　　23: pRhtHashs = new unsigned[nRhtCnt];<br /><br />　　24: //...b<br /><br />　　25: }<br /><br />　　26:<br /><br />　　27: //....<br /><br />　　28:<br /><br />　　29: if (...)<br /><br />　　30: {<br /><br />　　31: //因为下面这个函数可以释放资源，所以在它前面放弃对目标指针的管理权<br /><br />　　32: AUTO_FREE_DISABLE(pLftHashs);<br /><br />　　33: AUTO_FREE_DISABLE(pRhtHashs);<br /><br />　　34:<br /><br />　　35: //这个函数可以释放资源<br /><br />　　36: free_hash_arrays(pLftHashs, pRhtHashs);<br /><br />　　37: }<br /><br />　　38: }<br /><br />　　同样的，有时我们需要申请一个动态二维数组，所以也实现一个对应的auto_free_2D_ptr<br /><br />　　1: //auto_free_2D_ptr is only used for automation free memory of 2D array<br /><br />　　2: template<br /><br />　　3: class auto_free_2D_ptr<br /><br />　　4: {<br /><br />　　5: public:<br /><br />　　6: typedef enum {invalid, new_one, new_array, alloc_mem} EFLAG;<br /><br />　　7: auto_free_2D_ptr() { initialize(); }<br /><br />　　8: ~auto_free_2D_ptr() { free_ptr(); }<br /><br />　　9:<br /><br />　　10: ///set the pointer needed to automatically free<br /><br />　　11: inline void set_ptr( T** new_ptr_address,EFLAG new_eflag, int new_length_row )<br /><br />　　12: { free_ptr(); p_ptr = new_ptr_address; eflag = new_eflag; length_row = new_length_row; }<br /><br />　　13:<br /><br />　　14: //give up auto free memory<br /><br />　　15: inline void give_up() { initialize(); }<br /><br />　　16:<br /><br />　　17: protected:<br /><br />　　18: inline void initialize() { p_ptr = NULL; eflag = invalid; length_row = 0;}<br /><br />　　19: inline void free_ptr() throw()<br /><br />　　20: {<br /><br />　　21: if(!p_ptr || !(*p_ptr)) return;<br /><br />　　22:<br /><br />　　23: for(int i = 0; i &lt; length_row; i++)<br /><br />　　24: {<br /><br />　　25: if(!(*p_ptr)[i]) continue;<br /><br />　　26: switch(eflag)<br /><br />　　27: {<br /><br />　　28: case alloc_mem: { free((*p_ptr)[i]); break; }<br /><br />　　29: case new_one: { delete (*p_ptr)[i]; break; }<br /><br />　　30: case new_array: { delete[] (*p_ptr)[i]; break; }<br /><br />　　31: }<br /><br />　　32: (*p_ptr)[i] = NULL;<br /><br />　　33: }<br /><br />　　34: switch(eflag)<br /><br />　　35: {<br /><br />　　36: case alloc_mem: { free((*p_ptr)); break; }<br /><br />　　37: default: { delete[] (*p_ptr); break; }<br /><br />　　38: }<br /><br />　　39: (*p_ptr) = NULL, p_ptr = NULL;<br /><br />　　40: }<br /><br />　　41:<br /><br />　　42: protected:<br /><br />　　43: T** p_ptr; //!&lt; pointer to the address of the set pointer needed to automatically free<br /><br />　　44: EFLAG eflag; //!&lt; the type of allocation<br /><br />　　45: int length_row; //!&lt; the row length such as ptr[length_row][length_col]<br /><br />　　46:<br /><br />　　47: private:<br /><br />　　48: DISABLE_COPY_AND_ASSIGN(auto_free_2D_ptr);<br /><br />　　49: };<br /><br />　　50:<br /><br />　　51: #define AUTO_FREE_2D_ENABLE( class, ptrName, ptrType, rowNum ) \<br /><br />　　52: auto_free_2D_ptr auto_free_##ptrName; \<br /><br />　　53: auto_free_##ptrName.set_ptr(&amp;ptrName,auto_free_2D_ptr::ptrType, rowNum)<br /><br />　　54:<br /><br />　　55: #define AUTO_FREE_2D_DISABLE( ptrName ) AUTO_FREE_DISABLE( ptrName )<br /><br />　　下面是个例子<br /><br />　　1: void func(int row, int col)<br /><br />　　2: {<br /><br />　　3: if (!row &amp;&amp; !col)<br /><br />　　4: return;<br /><br />　　5:<br /><br />　　6: int **ptr = new int*[ row ];<br /><br />　　7: for( int r = 0; r &lt; row; ++r ) { ptr[r] = new int[ col ];}<br /><br />　　8:<br /><br />　　9: AUTO_FREE_2D_ENABLE( int, ptr, new_array, row );<br /><br />　　10:<br /><br />　　11: //....<br /><br />　　12: }<br /><br />　　到这里就结束了，有些码友可能会说，何必这么麻烦，boost内有很多智能指针供选择，用share_ptr, scoped_ptr, scoped_array，unique_ptr, auto_ptr 中的一个不就行了吗? 没错!如果你正在开发的代码中，允许用boost，并且在相关程序接口统一都用智能指针来管理、不会用到源对象指针的话，当然优先选boost，但是当你的代码中由于历史原因，有些接口不可变更，且new/delete, malloc/free都存在，而且依然需要使用源对象指针来完成大部分工作时，不妨试试我设计的这个阉割版的scoped_ptr/scoped_array。总之，根据自己的实际情况来选择合适的方案，如果标准方案不适用，就自己写一个<strong><a href="http://www.daan678.com/"><font color="#ffffdf" size="2">托福答案</font></a></strong> </p><img src ="http://www.cppblog.com/haosola/aggbug/208891.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/haosola/" target="_blank">HAOSOLA</a> 2014-11-16 09:05 <a href="http://www.cppblog.com/haosola/archive/2014/11/16/208891.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>将无声的视频剪辑加入应用程序</title><link>http://www.cppblog.com/haosola/archive/2014/11/16/208890.html</link><dc:creator>HAOSOLA</dc:creator><author>HAOSOLA</author><pubDate>Sun, 16 Nov 2014 00:55:00 GMT</pubDate><guid>http://www.cppblog.com/haosola/archive/2014/11/16/208890.html</guid><wfw:comment>http://www.cppblog.com/haosola/comments/208890.html</wfw:comment><comments>http://www.cppblog.com/haosola/archive/2014/11/16/208890.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/haosola/comments/commentRss/208890.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/haosola/services/trackbacks/208890.html</trackback:ping><description><![CDATA[<p><strong>使用多媒体</strong><br />C++Builder可在应用程序中加入多媒体组件。可以使用组件面板Win32页中的TAnimate组件或System页中的TMediaPlayer组件。需要在应用程序中加入无声的视频剪辑时，使用动画组件。需要把声音和/或视频剪辑加入应用程序时，使用媒体播放器组件。<br />本节讨论下列内容：<br />&#183; 将无声的视频剪辑加入应用程序。<br />&#183; 将声音和/或视频剪辑加入应用程序。<br /><br /><strong>将无声的视频剪辑加入应用程序</strong><br />使用C++Builder的动画控件可把无声的视频剪辑加入应用程序。<br /><br /><strong>要把无声的视频剪辑加入应用程序：</strong><br />1) 在组件面板的Win32页中双击动画控件的图标。这将自动把动画控件放在窗体上欲在其中显示视频剪辑的窗口中。<br />2) 使用对象观察器,选择Name属性并给动画控件输入一个新名字。调用动画控件时将使用这个名字（遵循标准的C++标识符命名规则）。当设置设计时属性及创建事件处理程序时，总是直接在对象观察器中工作。<br />3) 选择下列操作之一：<br />&#183; 选择CommonAVI属性并选择下拉列表中可用的AVI。<br />&#183; 或选择FileName属性并点击省略（...）按钮，从本地或网络目录中选择一个可用的AVI文件，并在OpenAVI对话框中点击Open。<br />&#183; 或使用ResName或ResID属性选择一个AVI资源。使用ResHandle指定包含由ResName或ResID属性标识的资源的模块。<br />这将把AVI文件载入内存。将Open属性设为true会把AVI剪辑的第一帧显示在屏幕上，直到使用Active属性或Play方法播放AVI剪辑。<br /><br />4) 设置Repetitions属性可设置AVI剪辑播放的重复次数。若其值为0，AVI剪辑会一直重复直到Stop方法被调用。<br />5) 修改动画控件的其他设置。例如,若要改变动画控件打开时显示的第一帧，可将StartFrame属性设为需要的帧。<br />) 使用下拉列表将Active属性设为true或编写事件处理程序以在运行时特定的事件发生时播放AVI剪辑。例如,要在一个按钮对象被点击时，应为按钮的OnClick事件编写事件处理程序。也可以调用Play方法指定播放AVI剪辑<strong><em><a href="http://www.yztrans.com/"><font color="#ffffdf" size="2">托福答案</font></a></em></strong><br /><br />注意若在Active属性设为true以后在修改窗体或窗体中的组件,Active属性将变为false，必须重新将它设为true。应仅在运行以前或在运行时做这些工作。加入无声视频剪辑的示例，假定想要在应用程序启动时将动画徽标作为初始的屏幕显示。在动画徽标播放完后这幅屏幕消失。要运行这个例子,先创造一个新工程并将Unit1.cpp文件保存为Frmlogo.cpp，以及将Project1.bpr文件保存为Logo.bpr。然后：<br /><br />1) 在组件面板的Win32页中双击动画组件图标。<br />2) 使用对象观察器,将其Name属性设为Logo1。<br />3) 选择其FileName属性,点击省略（...）按钮,从你的..\Examples\Coolstuf目录中选择cool.avi文件。<br /><br />然后在OpenAVI对话框中点击Open。这将把cool.avi文件载入内存。<br />4) 通过点击和拖动把动画控制框放置在窗体的右上角。<br />5) 将其Repetitions属性设为5 <strong><em><a href="http://www.yzyxedu.com/"><font color="#ffffdf" size="2">SAT答案</font></a></em></strong><br />) 点击窗体使其获得焦点并将其Name属性设为LogoForm1，Caption属性设为LogoWindow。然后减小窗体的高度以便将动画控件放到窗体正中。<br />7) 双击窗体的OnActivate事件并编写下列代码以当窗体在运行时获得焦点时播放AVI剪辑：<br /><br />Logo1&#8594;Active = true;<br /><br />8) 在组件面板Standard页上双击标签控件图标。选择它的Caption属性并输入&#8220;WelcometoCool Images4.0&#8221;。然后选择Font属性,点击省略（...）按钮并从字体对话框中选择字体样式：Bold,Size：18,Color：Navy，并单击OK。点击并拖动标签控件以将它放到窗体正中<strong><em><a href="http://www.daan678.com/"><font color="#ffffdf" size="2">托福答案</font></a></em></strong><br />9) 点击动画控件使其获得焦点。双击其OnStop事件并编写下列代码以当AVI文件停止时关闭窗体：<br />LogoForm1&#8594;Close();<br /><br />10) 选择RunRun执行动画徽标窗口。</p><img src ="http://www.cppblog.com/haosola/aggbug/208890.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/haosola/" target="_blank">HAOSOLA</a> 2014-11-16 08:55 <a href="http://www.cppblog.com/haosola/archive/2014/11/16/208890.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C/C++中容器vector使用方法</title><link>http://www.cppblog.com/haosola/archive/2014/09/20/208361.html</link><dc:creator>HAOSOLA</dc:creator><author>HAOSOLA</author><pubDate>Sat, 20 Sep 2014 07:13:00 GMT</pubDate><guid>http://www.cppblog.com/haosola/archive/2014/09/20/208361.html</guid><wfw:comment>http://www.cppblog.com/haosola/comments/208361.html</wfw:comment><comments>http://www.cppblog.com/haosola/archive/2014/09/20/208361.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/haosola/comments/commentRss/208361.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/haosola/services/trackbacks/208361.html</trackback:ping><description><![CDATA[　　此文总结常用vector操作，是前一篇的续作!只有代码，详细请看代码中的注释。<br />&nbsp;　　[cpp] view plaincopy/*<br />&nbsp;　　* file_name: vector_test.cpp<br />&nbsp;　　*<br />&nbsp;　　* Created on: 2014年6月28日 下午3:34:23<br />&nbsp;　　* Author: The_Third_Wave,<br />&nbsp;　　* Last modified: 2014年6月28日 下午3:34:23<br />&nbsp;　　#include<br />&nbsp;　　#include<br />&nbsp;　　#include "Headers/Myfunc.h"<br />&nbsp;　　#include "Headers/Person.h"<br />&nbsp;　　void output(const std::vector &amp;vec)<br />&nbsp;　　// 因为是输出而不是修改，定义形参为常量引用，提高可靠性(const)和效率(&amp;)!<br />&nbsp;　　{<br />&nbsp;　　std::cout &lt;&lt; "size: " &lt;&lt; vec.size()&lt;&lt; ", capacity: " &lt;<br />&nbsp;　　}<br />&nbsp;　　int main()<br />&nbsp;　　{<br />&nbsp;　　std::vector vec = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};<br />&nbsp;　　pr_vector(vec);<br />&nbsp;　　// 访问首位元素，原生方法之vec.front()和vec.back() 使用前一般最好做元素检查vec.size()<br />&nbsp;　　std::cout &lt;&lt; vec.front() &lt;&lt; std::ends &lt;&lt; vec.back() &lt;&lt; std::endl;<br />&nbsp;　　// 通过迭代器【注意:*(--vec.end())】， 使用前一般最好做元素检查vec.size()<br />&nbsp;　　std::cout &lt;&lt; *vec.begin() &lt;&lt; std::ends &lt;&lt; *(--vec.end()) &lt;&lt; std::endl;<br />&nbsp;　　auto a = vec.size(); // 让编译器自动分析表达式所属类型<br />&nbsp;　　auto b = vec.capacity(); // 不重新分配内存的话，vector可以保存多少元素<br />&nbsp;　　std::cout &lt;&lt; "size: " &lt;&lt; a &lt;&lt; std::ends &lt;&lt; "capacity: " &lt;<br />&nbsp;　　vec.reserve(50); // 分配至少能容纳n个元素的内存空间<br />&nbsp;　　output(vec);<br />&nbsp;　　vec.shrink_to_fit(); // 将capacity()减少为size()相同大小<br />&nbsp;　　output(vec);<br />&nbsp;　　vec.reserve(50); // 分配至少能容纳50个元素的内存空间<br />&nbsp;　　// 以下添加元素<br />&nbsp;　　for (decltype(vec.size()) ix = 0; ix != 40; ++ix) // decltype从表达式类型推导出变量类型<br />&nbsp;　　{<br />&nbsp;　　vec.push_back(ix*ix);<br />&nbsp;　　}<br />&nbsp;　　pr_vector(vec);<br />&nbsp;　　output(vec); // s输出证明没超过size，vector就不会分配超额的空间<br />&nbsp;　　// 我们不分配空间，看看自动管理<br />&nbsp;　　for (decltype(vec.size()) ix = 0; ix != 40; ++ix) // decltype从表达式类型推导出变量类型<br />&nbsp;　　{<br />&nbsp;　　vec.push_back(ix*ix);<br />&nbsp;　　}<br />&nbsp;　　pr_vector(vec);<br />&nbsp;　　output(vec); // 输出表示分配了超额的空间,【测试表明：分配的空间为当前的2倍，也就意味着越大越浪费】<br />&nbsp;　　// 以下重新初始化，开始插入操作的学习【并学习及几种赋值的操作方法】<br />&nbsp;　　std::vector vec2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; // 重新初始化<br />&nbsp;　　pr_vector(vec); pr_vector(vec2);<br />&nbsp;　　std::swap(vec, vec2); // 【快速】交换两容器的数据 swap(a, b)<br />&nbsp;　　pr_vector(vec); pr_vector(vec2);<br />&nbsp;　　vec2.assign(vec.begin(), vec.end()); // 参数为另外一个同类型vector的迭代器<br />&nbsp;　　pr_vector(vec); pr_vector(vec2);<br />&nbsp;　　vec.assign({0, 0, 0, 0, 0}); // 参数为初始化列表<br />&nbsp;　　pr_vector(vec);<br />&nbsp;　　vec.assign(10, 1); // 替换为10个1<br />&nbsp;　　pr_vector(vec);<br />&nbsp;　　// vector没有向头部插入数据的原生方法，只能通过insert()方法来操作，插入位置都为指针指向的前一个位置<br />&nbsp;　　// 具体有4种<br />&nbsp;　　// 第一种vec.insert(p, t); p为自身的迭代器，t为需要插入的值，返回值为指向新添加元素的迭代器<br />&nbsp;　　auto p = vec.insert(vec.begin() + vec.size()/2, 6688);<br />&nbsp;　　// 第二种vec.insert(p, n, t); p为自身的迭代器，插入n个t，返回值为指向新添加的第一个元素的迭代器，如果n为0，则返回p<br />&nbsp;　　vec.insert(p, 3, 4);<br />&nbsp;　　// 第三种vec.insert(p, b, e); p为自身的迭代器，b、e为同类型其他vec对象的迭代器，返回值为指向新添加的第一个元素的迭代器。范围为空，则返回p<br />&nbsp;　　vec.insert(p, vec2.crbegin(), vec2.crend()); // const 反向迭代器<strong><em><a href="http://www.yzyxedu.com/"><font color="#ffffe6">SAT答案</font></a></em></strong><br />&nbsp;　　// 第四种vec.insert(p, il); p为自身的迭代器，il为元素值列表，返回值为指向新添加的第一个元素的迭代器，列表为空，则返回p<br />&nbsp;　　vec.insert(vec.begin(), {9,8,7,6,5,4,3,2,1,0});<br />&nbsp;　　pr_vector(vec);<br />&nbsp;　　// 善用使用insert返回值，可以实现特定位置的反复插入,以上结果已说明<strong><em><a href="http://www.tfjy386.com/"><font color="#ffffe6">托福答案</font></a></em></strong><br />&nbsp;　　// emplace操作， 【c++11】emplace_front【vector没有】、emplace、emplace_back对应push_front【vector没有】、insert、push_back<br />&nbsp;　　std::vector per = {{"The_Third_Wave", 100, }}; // 类初始化+vector初始化，所以{{}， {}}必须的<br />&nbsp;　　per.emplace_back("The_Third_Wave", 188, );<br />&nbsp;　　per.emplace(per.begin() + 1, Person("The_Third_Wave", 168, ));<br />&nbsp;　　for (auto &amp;p: per)<br />&nbsp;　　{<br />&nbsp;　　print(std::cout, p);<br />&nbsp;　　}<br />&nbsp;　　// 删除操作，注意编译器不检查元素是否存在，所以坑自己填<br />&nbsp;　　// vec.pop_back(),vector没有pop_front()<br />&nbsp;　　vec = {0, 1, 2, 3, 4, 5, 6, 7, 8 ,9};<br />&nbsp;　　vec.pop_back();<br />&nbsp;　　pr_vector(vec);<br />&nbsp;　　// vec.erase(p)【删除迭代器p所指元素，返回值为：被删元素之后的迭代器。p指向尾部，返回尾后迭代器，如果p是尾后迭代器，那就坑死你了，恭喜!】<br />&nbsp;　　vec.erase(vec.begin() + 8);<br />&nbsp;　　pr_vector(vec);<br />&nbsp;　　// vec.erase(b, e)【删除迭代器b，e所指范围元素，返回值为：被删元素之后的迭代器。如果e是尾后迭代器，返回的还是尾后迭代器，不坑，恭喜!】<br />&nbsp;　　vec.erase(vec.begin() + 3 , vec.end() - 1);<br />&nbsp;　　pr_vector(vec);<br />&nbsp;　　// vec.clear()删除所有，返回值为void<br />&nbsp;　　vec.clear();<br />&nbsp;　　pr_vector(vec);<br />&nbsp;　　std::cout &lt;&lt; "上面有空行就对了!否则就是出错了。";<br />&nbsp;　　}<br />&nbsp;　　结果为：<br />&nbsp;<br />&nbsp;　　[cpp] view plaincopy<br />&nbsp;　　0 1 2 3 4 5 6 7 8 9<br />&nbsp;　　0 9<br />&nbsp;　　0 9<br />&nbsp;　　size: 10 capacity: 10<br />&nbsp;　　size: 10, capacity: 50<br />&nbsp;　　size: 10, capacity: 10<br />&nbsp;　　0 1 2 3 4 5 6 7 8 9 0 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400 441 484 529 576 625 676 729 784 841 900 961 1024 1089 1156 1225 1296 1369 1444 1521<br />&nbsp;　　size: 50, capacity: 50<br />&nbsp;　　0 1 2 3 4 5 6 7 8 9 0 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400 441 484 529 576 625 676 729 784 841 900 961 1024 1089 1156 1225 1296 1369 1444 1521 0 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400 441 484 529 576 625 676 729 784 841 900 961 1024 1089 1156 1225 1296 1369 1444 1521<br />&nbsp;　　size: 90, capacity: 100<br />&nbsp;　　0 1 2 3 4 5 6 7 8 9 0 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400 441 484 529 576 625 676 729 784 841 900 961 1024 1089 1156 1225 1296 1369 1444 1521 0 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400 441 484 529 576 625 676 729 784 841 900 961 1024 1089 1156 1225 1296 1369 1444 1521<br />&nbsp;　　0 1 2 3 4 5 6 7 8 9<br />&nbsp;　　0 1 2 3 4 5 6 7 8 9<br />&nbsp;　　0 1 2 3 4 5 6 7 8 9 0 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400 441 484 529 576 625 676 729 784 841 900 961 1024 1089 1156 1225 1296 1369 1444 1521 0 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400 441 484 529 576 625 676 729 784 841 900 961 1024 1089 1156 1225 1296 1369 1444 1521<br />&nbsp;　　0 1 2 3 4 5 6 7 8 9<br />&nbsp;　　0 1 2 3 4 5 6 7 8 9<br />&nbsp;　　0 0 0 0 0<br />&nbsp;　　1 1 1 1 1 1 1 1 1 1<br />&nbsp;　　9 8 7 6 5 4 3 2 1 0 1 1 1 1 1 9 8 7 6 5 4 3 2 1 0 4 4 4 6688 1 1 1 1 1<br />&nbsp;　　0 1 2 3 4 5 6 7 8<br />&nbsp;　　0 1 2 3 4 5 6 7<br />&nbsp;　　0 1 2 7<br />&nbsp;　　上面有空行就对了!否则就是出错了。<img src ="http://www.cppblog.com/haosola/aggbug/208361.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/haosola/" target="_blank">HAOSOLA</a> 2014-09-20 15:13 <a href="http://www.cppblog.com/haosola/archive/2014/09/20/208361.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++迭代器的两种实现方式</title><link>http://www.cppblog.com/haosola/archive/2014/08/25/208128.html</link><dc:creator>HAOSOLA</dc:creator><author>HAOSOLA</author><pubDate>Mon, 25 Aug 2014 13:36:00 GMT</pubDate><guid>http://www.cppblog.com/haosola/archive/2014/08/25/208128.html</guid><wfw:comment>http://www.cppblog.com/haosola/comments/208128.html</wfw:comment><comments>http://www.cppblog.com/haosola/archive/2014/08/25/208128.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/haosola/comments/commentRss/208128.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/haosola/services/trackbacks/208128.html</trackback:ping><description><![CDATA[<p>　　一、迭代器概述<br />　　这个标题其实有点"标题党"的含义，因为C++在标准库中的实现迭代器的方式只有一种，也就是为类定义begin（）和end（）函数，C++11增加了range for语句，可以用来遍历迭代器中的元素。实现迭代器的第二种方式，就是用C++模拟C#和Java中的迭代器模式，并且我们可以定义出自己的foreach语句。除此之外，迭代器可能还有很多种实现的方法，各个库也会多自己的迭代器的实现有所定义，在这里只要明白迭代器的本质意义即可。<br />　　迭代器，也称作游标，是一种设计模式，我们可以对它进行递增（或选择下一个）来访问容器中的元素，而无需知道它内部是如何实现的。<br />　　很多语言提供foreach语句来访问容器中的每一个元素，其实也就是调用容器中的迭代器，抽象地来说就是：<br />　　foreach （ 元素 : 容器 ） { &#8230; }<br />　　上面的代码等效于：<br />　　for （ 游标=获得迭代器的开头，获得迭代器的末尾； 游标 != 迭代器末尾； 游标移动一个单位 ） {&#8230;} // C++ 的迭代器模式<br />　　或者：<br />　　while （ 迭代器游标移动一个单位（返回是否存在下一个单位）） { &#8230; } // C#、Java的迭代器模式，可以用迭代器。Current之类的方法返回游标所指向的元素<br />　　二、C++中的迭代器实现方式<br />　　迭代器其实是一个类，要自定义一个迭代器，就要重载迭代器的！=、解引用（*）、++运算符，以便它在range for语句中使用。range for 是C++11中新增的语句，如我们对一个集合使用语句for （auto i : collection ） 时，它的含义其实为：<br />　　for （ auto __begin = collection.begin（）， auto __end = collection.end（）； __begin != __end（）； ++__begin ）<br />　　{ i = *__begin;<br />　　&#8230; //循环体<br />　　}<br />　　begin和end是集合的成员函数，它返回一个迭代器。如果让一个类可以有range for的操作，它必须： &#183; 拥有begin和end函数，它们均返回迭代器 &#183; end函数返回一个指向集合末尾，但是不包含末尾元素的值，即用集合范围来表示，一个迭代器的范围是 [ begin, end ） 一个左闭右开区间 对于迭代器，它的要求至少是： &#183; 必须重载++、！=和解引用（*）运算符；迭代器看起来会像一个指针 &#183; 迭代器必须可以通过++最后满足！=条件，这样才能够终止循环<br />　　下面给出最简单的实现代码。我们定义一个CPPCollection类，里面有个字符串数组，我们让它能够通过range for将每个字符串输出来。<br />　　#include &lt;IOSTREAM&gt;<br />　　class CPPCollection {<br />　　public:<br />　　class Iterator{<br />　　public:<br />　　int index;<br />　　CPPCollection&amp; outer;<br />　　Iterator（CPPCollection &amp;o, int i） : outer（o）， index（i） { }<br />　　void operator++（）{<br />　　index++;<br />　　}<br />　　std::string operator*（） const{<br />　　return outer.str[index];<br />　　}<br />　　bool operator !=（Iterator i）{<br />　　return i.index != index - 1;<br />　　}<br />　　};<br />　　public:<br />　　std::string str[10] {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"};<br />　　Iterator begin（） {<br />　　return Iterator（*this, 0）；<br />　　}<br />　　Iterator end（） {<br />　　return Iterator（*this, 9）；<br />　　}<br />　　};<br />　　我们定义了个内部的嵌套类Iterator,并为它重载了++、*、！=运算符。由于C++中的内部嵌套类与外围的类没有联系，为了访问外部类对象的值，我们必须要传入一个引用（或指针，本例中传入引用）。Iterator的自增方法其实就是增加内部的一个索引值。判断！=的方法是和另外一个迭代器做比较，这个迭代器一般是集合的末尾，当我们的索引值不等于末尾的索引值-1（ [begin, end） ）时，认为迭代器已经达到了末尾。 在CPPCollection类中，定义了begin（）、end（）分别返回开头、结束迭代器，调用如下代码：<br />　　CPPCollection cpc;<br />　　for （auto i : cpc）{<br />　　std::cout 《 i 《 std::endl;<br />　　}<br />　　即可遍历集合中的所有元素了。<br />　　在泛型算法中，为了对集合中的每一个元素进行操作，我们通常要传入集合的迭代器头、迭代器尾，以及谓词，例如std::find_if（vec.begin（）， vec.end（）， &#8230;），这种泛型算法其实就是在迭代器的首位反复迭代，然后运行相应的行为。<br />　　三、模拟C#（Java）中的迭代器实现方式<br />　　C#和Java的迭代器实现方式较为类似，在这里以C#为例，我用C++来模拟它的实现。C#中，可迭代的类继承IEnumerable接口，它声明了一个方法GetEnumerator,返回一个迭代器。迭代器继承IEnumerator接口，接口定义了MoveNext（）方法：将游标移动一个单位，并返回是否还可以移动；Reset（）方法：游标归位； Current属性：返回当前游标所指向的值（在C#中，Current是一个属性，为了模拟它，在C++中将它定义为一个函数），并且用只包含抽象函数的类来模拟接口<strong><em><a href="http://www.daan678.com/"><font color="#ffffe8">托福答案</font></a></em></strong><br />　　#define interface struct<br />　　template &lt;TYPENAME T&gt;<br />　　interface IEnumerator {<br />　　virtual T&amp; Current（） = 0;<br />　　virtual bool MoveNext（） = 0;<br />　　virtual void Reset（） = 0;<br />　　virtual ~IEnumerator&lt;T&gt;（） { };<br />　　};<br />　　template &lt;TYPENAME T&gt;<br />　　interface IEnumerable {<br />　　virtual IEnumerator&lt;T&gt;&amp; GetEnumerator（） = 0;<br />　　virtual ~IEnumerable （） { };<br />　　};<br />　　为了自定义一个foreach"关键字",可以用宏定义来进行迭代器的等效替代：<br />　　#define foreach（item, collection） \<br />　　auto &amp;__item_enumerator = collection.GetEnumerator（）； \<br />　　__item_enumerator.Reset（）； \<br />　　while （item = __item_enumerator.Current（）， __item_enumerator.MoveNext（））<br />　　当我们使用foreach的时候，宏展开自动展开为调用集合类collection的GetEnumerator（）函数来获得一个迭代器的引用，复位之，并将当前值赋予item,迭代器向前移动。需要注意的是，我们在代码中不能重复定义__item_enumerator变量，且类型一定要为auto&amp;:auto变量不会保留引用符号，且如果不是引用（或指针），会触发拷贝构造函数，从而失去对象的多态性。 如同刚才一样，我们定义一个集合类：<br />　　class CSharpCollection : public IEnumerable&lt;STD::STRING&gt;{<br />　　public:<br />　　class Enumerator : public IEnumerator&lt;STD::STRING&gt; {<br />　　public:<br />　　CSharpCollection &amp;outer; //外部类<br />　　int index = 0; //下标<br />　　Enumerator （CSharpCollection &amp;o） : outer（o）{<br />　　}<br />　　bool MoveNext（） override {<br />　　index++;<br />　　if （index &lt;= 10） return true;<br />　　delete this;<br />　　return false;<br />　　}<br />　　void Reset（） override {<br />　　index = 0;<br />　　}<br />　　std::string &amp;Current（） override {<br />　　return outer.str[index];<br />　　}<br />　　~Enumerator （）{<br />　　std::cout 《 "Enumerator 被析构" 《 std::endl;<br />　　}<br />　　};<br />　　public:<br />　　std::string str[10] {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"};<br />　　IEnumerator&lt;STD::STRING&gt;&amp; GetEnumerator（） override{<br />　　return *new Enumerator（*this）；<br />　　}<br />　　};<br />　　需要注意的是，凡是体现多态性的函数，返回值必须为引用或者指针，且不得为栈中的临时变量，因此我们调用完GetEnumerator（）后，要将生成的迭代器删除，删除的代码写在了MoveNext（）内，当游标不可移动的时候，迭代器被删除<strong><em><a href="http://www.tfjy386.com/"><font color="#ffffe8">托福答案</font></a></em></strong><br />　　以后就可以用自己的foreach宏定义来遍历元素了：<br />　　std::string a;<br />　　CSharpCollection csc;<br />　　IEnumerable&lt;STD::STRING&gt;&amp; refcsc = csc;<br />　　foreach （a , refcsc ）{<br />　　std::cout 《 a 《 std::endl;<br />　　}<br />　　上面代码的第三行意在说明，如果一个类中继承了IEnumerable类，它一定是可迭代的，可以调用它的Reset（）、MoveNext（）、Current（），也可以用我们刚刚写的foreach来进行遍历。</p><img src ="http://www.cppblog.com/haosola/aggbug/208128.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/haosola/" target="_blank">HAOSOLA</a> 2014-08-25 21:36 <a href="http://www.cppblog.com/haosola/archive/2014/08/25/208128.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>数据格式 int与char的存储方式</title><link>http://www.cppblog.com/haosola/archive/2014/08/25/208126.html</link><dc:creator>HAOSOLA</dc:creator><author>HAOSOLA</author><pubDate>Mon, 25 Aug 2014 13:15:00 GMT</pubDate><guid>http://www.cppblog.com/haosola/archive/2014/08/25/208126.html</guid><wfw:comment>http://www.cppblog.com/haosola/comments/208126.html</wfw:comment><comments>http://www.cppblog.com/haosola/archive/2014/08/25/208126.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/haosola/comments/commentRss/208126.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/haosola/services/trackbacks/208126.html</trackback:ping><description><![CDATA[<p>有时候错误的代码会让我们更深刻的理解一门编程语言，所以在Learn c the hard way一书中，作者不停的让我们把正确的代码破坏掉，发挥我们的好奇心与想象力，去发觉c语言的一些特性。<br />为了弄清弄清c中int与char型变量存储的异同和gcc对无'\0'字符数组的的反应，修改了Learn c the hard way书中的ex8<br />代码如下<br />#include &lt;stdio.h&gt;<br />int main（int argc,char *argv[]）<br />{<br />int areas[] = {10, 12, 13, 14, 20};<br />char name [] = "Zed";<br />char full_name[] = {<br />'Z', 'e', 'd',<br />' ', 'A', '.', ' ',<br />'S', 'h', 'a', 'w'<br />};<br />unsigned int interg[2];<br />interg[0] = '\0' * 0x01000000 + 'A'*0x00010000 + 'B' * 0x00000100 + 'C' * 0x00000001;<br />unsigned int inter = interg[0];<br />// WARNING: On some system you may have to change the<br />// %ld in this code to a %u since it will use unsignt ints<br />printf（"The size of an int: %ld\n",sizeof（int））；<br />printf（"The siez of areas （int[]）：%ld\n",<br />sizeof（areas））；<br />printf（"The number of ints in areas: %ld\n",<br />sizeof（areas）/sizeof（int））；<br />printf（"The first area is %d, the 2nd %d.\n",<br />areas[0],areas[10]）；<br />printf（"The size of a char: %ld\n",sizeof（char））；<br />printf（"The size of name （char[]）： %ld\n",<br />sizeof（name）/sizeof（char））；<br />printf（"The size of full_name （char[]）： %ld\n",<br />sizeof（full_name））；<br />printf（"The number of chars: %ld\n",<br />sizeof（full_name） / sizeof（char））；<br />// !!!其中full_name未设置null结尾<br />printf（"name=\"%s\" and full_name=\"%s\"\n",<br />name, full_name）；<br />// test 证明了gcc把未初始化的地方初始为0<br />printf（"the char after the full_name（shuold be error）：%c\n",full_name[12]）；<br />// 用 int 存储字符串<br />printf（"interg=%X\n", interg）；<br />printf（"inter=\"%s\"\n", &amp;inter）；<br />printf（"interg=\"%s\"\n", interg）；<br />return 0;<br />}<br />重点在于<br />char full_name[] = {<br />'Z', 'e', 'd',<br />' ', 'A', '.', ' ',<br />'S', 'h', 'a', 'w'<br />};<br />unsigned int interg[2];<br />interg[0] = '\0' * 0x01000000 + 'A'*0x00010000 + 'B' * 0x00000100 + 'C' * 0x00000001;<br />unsigned int inter = interg[0];<br />可以看到full_name字符串初始化的时候是没有null结尾的，如果的vc中编译并以字符串形式输出的话会跟上乱码<br />interg的数组第一个元素中我分别在四个字节中放入了'\0'（即0）、'A'、'B'、'C',第二个元素未初始化。<br />inter中放的是同样的数字。<br />输出如下：<br />The size of an int: 4<br />The siez of areas （int[]）：20<br />The number of ints in areas: 5<br />The first area is 10, the 2nd -1493946707.<br />The size of a char: 1<br />The size of name （char[]）： 4<br />The size of full_name （char[]）： 11<br />The number of chars: 11<br />name="Zed" and full_name="Zed A. Shaw"<br />the char after the full_name（shuold be error）：<br />interg=6EB11E70<br />inter="CBA"<br />interg="CBA"<br />可以看到以字符串输出的full_name并没有因为无null结尾而输出乱码或错误，在csdn论坛上了解到可能是因为gcc将字符串存储区全部初始化为零。<br />PS:对于数组的初始化，可以只初始化一个元素，就可以使其他元素为零（Learn c the hard way）；<br />inter以字符串输出的结果是按我赋值的倒序输出的，这个是因为小端的数据存储造成的<strong><em><a href="http://www.tfjy386.com/"><font color="#ffffe8">托福答案</font></a></em></strong></p><img src ="http://www.cppblog.com/haosola/aggbug/208126.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/haosola/" target="_blank">HAOSOLA</a> 2014-08-25 21:15 <a href="http://www.cppblog.com/haosola/archive/2014/08/25/208126.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C语言/C++递归算法</title><link>http://www.cppblog.com/haosola/archive/2014/05/30/207148.html</link><dc:creator>HAOSOLA</dc:creator><author>HAOSOLA</author><pubDate>Fri, 30 May 2014 01:53:00 GMT</pubDate><guid>http://www.cppblog.com/haosola/archive/2014/05/30/207148.html</guid><wfw:comment>http://www.cppblog.com/haosola/comments/207148.html</wfw:comment><comments>http://www.cppblog.com/haosola/archive/2014/05/30/207148.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/haosola/comments/commentRss/207148.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/haosola/services/trackbacks/207148.html</trackback:ping><description><![CDATA[<p>　　递归算法也是C语言算法中一个比较简单与常用的算法，本文我们就来谈谈递归算法，我们首先了解一下什么是递归算法，关于递归算法的概念只有一句话：一个过程（或函数）直接或间接调用自己本身，这种过程（或函数）叫递归过程（或函数）<br />　　我们再来看看递归算法的特点：<br />　　（1） 递归就是在过程或函数里调用自身。<br />　　（2） 在使用递归策略时，必须有一个明确的递归结束条件，称为递归出口。<br />　　（3） 递归算法解题通常显得很简洁，但递归算法解题的运行效率较低。所以一般不提倡用递归算法设计程序。<br />　　（4） 在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。所以一般不提倡用递归算法设计程序。<br />　　再看看递归的分类：<br />　　直接递归<br />　　程序设计中，过程或函数直接或者间接调用自己，就被称为递归调用。子程序直接调用自己，这称为直接递归；嵌套关系的子程序A和B,内层的B调用外层的A,这是间接低归；平级关系的子程序A和B,其中A调用了B,B调用了A,这也是间接递归，不过，这种间接递归用到了"超前引用"的规则。<br />　　下面，博主找到了一些有关于递归算法的题目。我们看看第一个题目。<br />　　这是一道很经典的题目：阶乘。相信大部分的人都知道什么是阶乘，但是你如果不知道的话，不妨参见一下百度百科：阶乘。<br />　　好了，其实这题非常简单，我们只需要一个函数即可解决问题。<br />　　1 int recursive（int i）<br />　　2 {<br />　　3 int sum = 0;<br />　　4 if （0 == i）<br />　　5 return （1）；<br />　　6 else<br />　　7 sum = i * recursive（i-1）；<br />　　8 return sum;<br />　　9 }<br />　　就是上面的递归函数，这题我就不浪费篇幅讲解了，重点放在第二个例题：汉诺塔问题。<br />　　一个庙里有三个柱子，第一个有64个盘子，从上往下盘子越来越大。要求庙里的老和尚把这64个盘子全部移动到第三个柱子上。移动的时候始终只能小盘子压着大盘子。而且每次只能移动一个。1、此时老和尚（后面我们叫他第一个和尚）觉得很难，所以他想：要是有一个人能把前63个盘子先移动到第二个柱子上，我再把最后一个盘子直接移动到第三个柱子，再让那个人把刚才的前63个盘子从第二个柱子上移动到第三个柱子上，我的任务就完成了，简单。所以他找了比他年轻的和尚（后面我们叫他第二个和尚），命令：<br />　　&#9312; 你把前63个盘子移动到第二柱子上<strong><em><a href="http://www.jamo123.com/"><font color="#ffffd7">托福答案</font></a></em></strong><br />　　&#9313; 然后我自己把第64个盘子移动到第三个柱子上后<br />　　&#9314; 你把前63个盘子移动到第三柱子上<br />　　2、第二个和尚接了任务，也觉得很难，所以他也和第一个和尚一样想：要是有一个人能把前62个盘子先移动到第三个柱子上，我再把最后一个盘子直接移动到第二个柱子，再让那个人把刚才的前62个盘子从第三个柱子上移动到第三个柱子上，我的任务就完成了，简单。所以他也找了比他年轻的和尚（后面我们叫他第三和尚），命令：<br />　　&#9312; 你把前62个盘子移动到第三柱子上<br />　　&#9313; 然后我自己把第63个盘子移动到第二个柱子上后<br />　　&#9314; 你把前62个盘子移动到第二柱子上<br />　　3、第三个和尚接了任务，又把移动前61个盘子的任务依葫芦话瓢的交给了第四个和尚，等等递推下去，直到把任务交给了第64个和尚为止（估计第64个和尚很郁闷，没机会也命令下别人，因为到他这里盘子已经只有一个了）。<br />　　4、到此任务下交完成，到各司其职完成的时候了。完成回推了：<br />　　第64个和尚移动第1个盘子，把它移开，然后第63个和尚移动他给自己分配的第2个盘子。<br />　　第64个和尚再把第1个盘子移动到第2个盘子上。到这里第64个和尚的任务完成，第63个和尚完成了第62个和尚交给他的任务的第一步。<br />　　从上面可以看出，只有第64个和尚的任务完成了，第63个和尚的任务才能完成，只有第2个和尚----第64个和尚的任务完成后，第1个和尚的任务才能完成。这是一个典型的递归问题。 现在我们以有3个盘子来分析：<br />　　第1个和尚命令：<br />　　&#9312; 第2个和尚你先把第一柱子前2个盘子移动到第二柱子。（借助第三个柱子）<br />　　&#9313; 第1个和尚我自己把第一柱子最后的盘子移动到第三柱子。<br />　　&#9314; 第2个和尚你把前2个盘子从第二柱子移动到第三柱子。<br />　　很显然，第二步很容易实现<strong><em><a href="http://www.daan678.com/"><font color="#ffffd7">托福答案</font></a></em></strong><br />　　其中第一步，第2个和尚他有2个盘子，他就命令：<br />　　&#9312; 第3个和尚你把第一柱子第1个盘子移动到第三柱子。（借助第二柱子）<br />　　&#9313; 第2个和尚我自己把第一柱子第2个盘子移动到第二柱子上。<br />　　&#9314; 第3个和尚你把第1个盘子从第三柱子移动到第二柱子。<br />　　同样，第二步很容易实现，但第3个和尚他只需要移动1个盘子，所以他也不用在下派任务了。（注意：这就是停止递归的条件，也叫边界值）<br />　　第三步可以分解为，第2个和尚还是有2个盘子，命令：<br />　　&#9312; 第3个和尚你把第二柱子上的第1个盘子移动到第一柱子。<br />　　&#9313; 第2个和尚我把第2个盘子从第二柱子移动到第三柱子。<br />　　&#9314; 第3个和尚你把第一柱子上的盘子移动到第三柱子。<br />　　分析组合起来就是：1&#8594;3 1&#8594;2 3&#8594;2 借助第三个柱子移动到第二个柱子 |1&#8594;3 | 2&#8594;1 2&#8594;3 1&#8594;3借助第一个柱子移动到第三个柱子|共需要七步。<br />　　如果是4个盘子，则第一个和尚的命令中第1步和第3步各有3个盘子，所以各需要7步，共14步，再加上第1个和尚的1步，所以4个盘子总共需要移动7+1+7=15步，同样，5个盘子需要15+1+15=31步，6个盘子需要31+1+31=64步&#8230;&#8230;由此可以知道，移动n个盘子需要（2的n次方）-1步。<br />　　从上面整体综合分析可知把n个盘子从1座（相当第一柱子）移到3座（相当第三柱子）：<br />　　（1）把1座上（n-1）个盘子借助3座移到2座。<br />　　（2）把1座上第n个盘子移动3座。<br />　　（3）把2座上（n-1）个盘子借助1座移动3座。<br />　　下面用hanoi（n,a,b,c）表示把1座n个盘子借助2座移动到3座。<br />　　很明显： （1）步上是 hanoi（n-1,1,3,2） （3）步上是 hanoi（n-1,2,1,3）<br /><br />　　用C语言表示出来，就是：<br />　　1 #include &lt;stdio.h&gt;<br />　　2 int method（int n,char a, char b）<br />　　3 {<br />　　4 printf（"number%dform%cto%c"n",n,a,b）；<br />　　5 return 0;<br />　　6 }<br />　　7 int hanoi（int n,char a,char b,char c）<br />　　8 {<br />　　9 if（ n==1 ） move （1,a,c）；<br />　　10 else<br />　　11 {<br />　　12 hanoi（n-1,a,c,b）；<br />　　13 move（n,a,c）；<br />　　14 hanoi（n-1,b,a,c）；<br />　　15 };<br />　　16 return 0;<br />　　17 }<br />　　18 int main（）<br />　　19 {<br />　　20 int num;<br />　　21 scanf（"%d",&amp;num）；<br />　　22 hanoi（num,'A','B','C'）；<br />　　23 return 0;<br />　　24 }<br />　　这就是递归算法，其实，在C语言中，递归算法比枚举法还要实用，但是这两种算法都很简单。</p><img src ="http://www.cppblog.com/haosola/aggbug/207148.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/haosola/" target="_blank">HAOSOLA</a> 2014-05-30 09:53 <a href="http://www.cppblog.com/haosola/archive/2014/05/30/207148.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C++二维数组动态内存分配</title><link>http://www.cppblog.com/haosola/archive/2014/05/28/207135.html</link><dc:creator>HAOSOLA</dc:creator><author>HAOSOLA</author><pubDate>Wed, 28 May 2014 12:29:00 GMT</pubDate><guid>http://www.cppblog.com/haosola/archive/2014/05/28/207135.html</guid><wfw:comment>http://www.cppblog.com/haosola/comments/207135.html</wfw:comment><comments>http://www.cppblog.com/haosola/archive/2014/05/28/207135.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/haosola/comments/commentRss/207135.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/haosola/services/trackbacks/207135.html</trackback:ping><description><![CDATA[<p>　　对于二维数组和二维指针的内存的分配<br />　　这里首选说一下一维指针和一维数组的内存分配情况。<br />　　一维：<br />　　数组：形如int a[5];这里定义了一个一维数组a,并且数组的元素个数是5,这里的a是这五个元素的整体表示，也就是通过a我们能找到这五个元素。注意：a是代表数组第一个元素的首地址。&amp;a是代表数组的地址，虽然它们的值相同。<br />　　指针： int *p = NULL;这里p是一个指针，它指向的是计算<br />　　机内一块存储int类型的内存。P = a;就是让p等于刚才申请的数组的第一个元素的地址。所以通过p我们也能找到那5个元素所以P[i]跟a[i]的作用一样。<br />　　注意：<br />　　1:int *p = NULL; p的大小在32位机器是4,即使p=a;之后p的sizeof（p）仍然等于4.<br />　　2:在声明之后，数组必须分配内存进行初始化。而指针一般是动态分配其指向的内存。<br />　　3:不要混淆指针和数组，指针就是指针，数组就是数组，只是数组在一定条件下可以转换成指针。不要将指针和数组混淆。（例如：指针有++,--操作，数组则不可以）。<br />　　一维指针的动态内存分配：<br />　　int *p = NULL;<br />　　p = new int[N];<br />　　千万别忘了delete<br />　　delete [] p;<br />　　p = NULL;<br />　　二维数组的内存分配<br />　　int a[2][3]; 这里分配了一个2X3=6个int大小的数组。二维数组的第二个维度3不能省略。<br />　　二维数组的内存在计算机内也是连续的一片地址，只不过每3个元素构成一个一维数组a[i],这里的a[i]代表维度为3的数组的第一个元素的地址。所以a[i][j]的访问跟a[i]的访问也就清楚了。这里的a[i]其实是一个一维数组的第一个元素的地址。<br />　　对于二维数组做实参，我们通常用一维指针处理，例如：<br />　　1 #include<br />　　2 void test（int *p）<br />　　3 {<br />　　4 for （int i = 0;i&lt;3;++i）<br />　　5 {<br />　　6 for（int j = 0;j&lt;3;++j）<br />　　7 {<br />　　8 std::cout《*（p+3*i+j）； //一维处理<br />　　9 }<br />　　10 }<br />　　11 }<br />　　12 int main（void）<br />　　13 {<br />　　14 int a[3][3]={1,2,3,4,5,6,7,0,0};<br />　　15 test（（int*）a）； //将二维数组当做一维处理<br />　　16 system（"pause"）；<br />　　17 return 0;<br />　　18 }<br />　　这些想必书上讲的都非常清楚。<br />　　二维数组的C++动态内存分配。<br />　　二维指针的动态数组分配：二维指针类似指针数组的分配<br />　　int **p;<br />　　1 #include<br />　　2 int main（void）<br />　　3 {<br />　　4 int **p = NULL; //这里申请一个3x4的二维数组<br />　　5 p = new int *[3]; //分配一维指针，分配三个int* 类型的一维指针。<br />　　6 for （int i = 0;i &lt; 3; ++i）<br />　　7 {<br />　　8 p[i] = new int[4];<br />　　9 }<br />　　10 for （int i = 0; i &lt; 3; ++i）<br />　　11 {<br />　　12 for（int j = 0; j &lt; 4 ; ++j）<br />　　13 {<br />　　14 p[i][j] = i*j;<br />　　15 std::cout&lt;<br />　　26 return 0;<br />　　27 }<br />　　指针数组的动态内存分配<br />　　指针数组的动态内存分配只需要对指针数组的数组元素指针分别分配内存即可，比二维指针的分配少了一个环节。<br />　　1 #include<strong><em><a href="http://www.daan678.com/"><font color="#ffffdf" size="2">托福答案</font></a></em></strong><br />　　2 int main（void）<br />　　3 {<br />　　4 int *a[3]; //申请含有三个int* 类型的指针数组<br />　　5 //跟二维指针不同的是，这里数组a不用手动申请内存<br />　　6 for （int i = 0;i &lt; 3;++i） //申请一个3x4的空间<br />　　7 {<br />　　8 a[i] = new int[4];<br />　　9 }<br />　　10 for （int i = 0; i&lt;3 ;++i）<br />　　11 {<br />　　12 for （int j = 0; j&lt;4; ++j）<br />　　13 {<br />　　14 a[i][j] = i*j;<br />　　15 std::cout&lt;<br />　　25 return 0;<br />　　26 }<br />　　数组指针的动态内存分配<br />　　数组指针就是指向数组的指针，说白了就是指向一个数组整体，因此分配的时候直接申请一片内存地址即可。跟二维数组的静态分配类似。<br />　　1 // Karllen<br />　　2 int main（void）<br />　　3 {<br />　　4 int （*a）[4]; //这里的4是第二维的维度，a的增量的基数为4个int<br />　　5 a = new int[3][4];<br />　　6 delete []a;<br />　　7 a = NULL;<br />　　8 return 0;<br />　　9 }</p><img src ="http://www.cppblog.com/haosola/aggbug/207135.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/haosola/" target="_blank">HAOSOLA</a> 2014-05-28 20:29 <a href="http://www.cppblog.com/haosola/archive/2014/05/28/207135.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C/C++混合编程--extern “C” 使用方法详解</title><link>http://www.cppblog.com/haosola/archive/2014/04/26/206722.html</link><dc:creator>HAOSOLA</dc:creator><author>HAOSOLA</author><pubDate>Sat, 26 Apr 2014 05:45:00 GMT</pubDate><guid>http://www.cppblog.com/haosola/archive/2014/04/26/206722.html</guid><wfw:comment>http://www.cppblog.com/haosola/comments/206722.html</wfw:comment><comments>http://www.cppblog.com/haosola/archive/2014/04/26/206722.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/haosola/comments/commentRss/206722.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/haosola/services/trackbacks/206722.html</trackback:ping><description><![CDATA[<p>　　其实在网上关于这个问题已经讨论很多了，但是大多都是重复的，确实讲解的很详细，还指出了怎么用是错误的，本来记忆就不怎么样，所以对于记忆这些错误的用法更是讨厌，还不如记忆一种通用的而且比较规范、代码阅读起来比较舒适的方法，下面我们开始吧！！！<br />　　C、C++密不可分，平时使用更多的是C,但有时候却少不了C++,而且是C、C++混搭（混合编程）在一起的，比如，RTP视频传输，live555多媒体播放等都是C++下的，他需要调用JRTPLIB库，再比如，我那邮件发送，我也用C++写的，定义了一个Email对象，包含了成员：收发邮件地址，用户名，密码等，以及方法：邮件头、Base64编码和邮件发送这些操作，很好用，所以，很多时候，C++还是蛮不错的&#8230;但，*.c与*.cpp文件混搭在一起，不是那么的简单，.<br />　　一、extern"C"的作用（最重点）<br />　　1. extern "C"的真实目的是实现类C和C++的混合编程。extern "C"是由C++提供的一个连接交换指定符号，用于告诉C++这段代码是C函数。extern "C"后面的函数不使用的C++的名字修饰，而是用C.这是因为C++编译后库中函数名会变得很长，与C生成的不一致，造成C++不能直接调用C函数<strong><em><a href="http://www.yztrans.com/"><font color="#ffffdd">托福答案</font></a></em></strong><br />　　2.C++语言支持函数重载，C语言不支持函数重载。函数被C++编译后在库中的名字与C语言的不同。假设某个函数的原型为：void foo（int x, int y）；该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。C++提供了C连接交换指定符号extern"C"来解决名字匹配问题。<br />　　3.被extern "C"限定的函数或变量是extern类型的；extern是C/C++语言中表明函数和全局变量作用范围（可见性）的关键字，该关键字告诉编译器，其声明的函数和变量可以在本模块或其它模块中使用。被extern "C"修饰的变量和函数是按照C语言方式编译和连接的<strong><em><a href="http://www.jamo123.com/"><font color="#ffffdd">雅思答案</font></a></em></strong><br />　　4.与extern对应的关键字是static,被它修饰的全局变量和函数只能在本模块中使用。因此，一个函数或变量只可能被本模块使用时，其不可能被extern "C"修饰。<br />　　二、extern"C"与__cplusplus<br />　　#ifdef __cplusplus<br />　　extern "C" {<br />　　#endif<br />　　#ifdef __cplusplus<br />　　}<br />　　#endif<br />　　Cplusplus（C plus plus）即"C++",用于C++文档的头文件中，上面代码的意思是：如果是C++文件（*.cpp）后缀，则使用extern "C",在C++项目中应用的非常广泛。即使用gcc编译器编译，函数名为C类型如_foo.个人认为，搞懂了这两个关键字，尤其是理解extern "C"<br />　　extern "C" 是为了C与C++混合编程而设立的关键字，假如你已经知道了关于extern "C" 的一些使用方法，想很快掌握使用策略，下面从两个角度说明：<br />　　1）在C++程序中调用C程序，比如在CPP文件中使用C文件的某一个函数，那么可以肯定的是所使用的函数肯定是按照C语言的编译方式编译，那么仅仅通告CPP文件按照C语言调用函数的方式调用即可，而且不用再重新编译C函数，在函数所在头文件中添加extern "C"关键字，将这个头文件包含到CPP文件即可。也就是如下所示：<br />　　#ifdef __cplusplus<br />　　extern "C" {<br />　　#endif<br />　　/*&#8230; 函数声明*/<br />　　#ifdef __cplusplus<br />　　}<br />　　#endif<br />　　稍微举例说明一下：<br />　　?<br />　　// cheader.h<br />　　#ifndef _C_HEADER_<br />　　#define _C_HEADER_<br />　　#ifdef __cplusplus<br />　　extern "C" {<br />　　#endif<br />　　#include &lt;STDIO.H&gt;<br />　　#include &lt;STDLIB.H&gt;<br />　　int add（int a,int b）；<br />　　#ifdef __cplusplus<br />　　}<br />　　#endif<br />　　#endif<br />　　// cfunc.c<br />　　//#include "header.h"<br />　　int add（int a,int b）<br />　　{<br />　　return a+b;<br />　　}<br />　　//main.cpp<br />　　#include &lt;IOSTREAM&gt;<br />　　#include "header.h"<br />　　using namespace std;<br />　　int main（）<br />　　{<br />　　int a,b=0;<br />　　b= add（2,3）；<br />　　cout《B《ENDL; pre &lt; } 0; return&gt;&lt;BR&gt;<br />　　由于。c文件中的函数就是按照C语言编译方式进行编译的，所以不包括头文件也是可以的，但是在头文件声明的时候必须进行说明，因为在CPP文件中包括的这个头文件会按照C语言的方式查找函数，不然就会按照C++的方式查找函数add&lt;BR&gt;<br />　　&lt;P&gt;&lt;STRONG&gt;2）在C程序中使用CPP编译的函数，这样需要重新编译函数库，在函数声明的头文件中也是如上声明，然后再函数所在的CPP文件添加上述头文件，直接编译即可，这个时候虽说在CPP文件编译，但是是安装C语言的方式编译，在C文件中同样添加上述头文件即可。&lt;/STRONG&gt;&lt;/P&gt;<br />　　&lt;P&gt;&lt;STRONG&gt;稍微举例说明一下：&lt;/STRONG&gt;&lt;/P&gt;<br />　　&lt;P&gt;&lt;STRONG&gt;&lt;/STRONG&gt;&lt;/P&gt;<br />　　&lt;PRE class=brush:java;&gt;// cheader.h头文件<br />　　#ifndef _C_HEADER_<br />　　#define _C_HEADER_<br />　　#ifdef __cplusplus<br />　　extern "C" {<br />　　#endif<br />　　#include &lt;STDIO.H&gt;<br />　　#include &lt;STDLIB.H&gt;<br />　　int sub（int a,int b）；<br />　　#ifdef __cplusplus<br />　　}<br />　　#endif<br />　　#endif<br />　　//cppsub.cpp<br />　　#include "header.h"<br />　　int sub（int a,int b）<br />　　{<br />　　return a-b;<br />　　}<br />　　/*但是如果将头文件的那一行注释掉，也会在连接时出错，因为在头文件中已经说明使用C语言的方式编译，但是不说明这一点，会使用C++编译方式进行<br />　　如果不想使用头文件，那么可以将int sub（int a,int b） 换为 extern "C" int sub（int a,int b） ,但是extern "C"在。c文件时不可以出现*/<br />　　//cmain.c<br />　　#include "header.h"<br />　　int main（）<br />　　{<br />　　int a=0,b=0;<br />　　b = sub（2,3）；<br />　　printf（"b is %d\r\n",a,b）；<br />　　return 0;<br />　　}<br />　　&lt;/PRE&gt;<br />　　但是在使用的时候也是有一定的规范的：&lt;BR&gt;<br />　　//C++头文件 cppExample.h&lt;BR&gt;<br />　　#ifndef CPP_EXAMPLE_H&lt;BR&gt;<br />　　#define CPP_EXAMPLE_H&lt;BR&gt;<br />　　extern "C" int add（ int x, int y ）；&lt;BR&gt;<br />　　#endif<br />　　&lt;BR&gt;<br />　　//C++实现文件 cppExample.cpp&lt;BR&gt;<br />　　#include "cppExample.h"&lt;BR&gt;<br />　　int add（ int x, int y ）&lt;BR&gt;<br />　　{&lt;BR&gt;<br />　　return x + y;&lt;BR&gt;<br />　　}<br />　　&lt;BR&gt;<br />　　/* C实现文件 cFile.c&lt;BR&gt;<br />　　/* 这样会编译出错：#include "cExample.h" */&lt;BR&gt;<br />　　extern int add（ int x, int y ）；&lt;BR&gt;<br />　　int main（ int argc, char* argv[] ）&lt;BR&gt;<br />　　{&lt;BR&gt;<br />　　add（ 2, 3 ）； &lt;BR&gt;<br />　　return 0;&lt;BR&gt;<br />　　}<br />　　note: 如果 cppEample.h 改成：<br />　　#ifndef CPP_EXAMPLE_H&lt;BR&gt;<br />　　#define CPP_EXAMPLE_H<br />　　#ifdef __cplusplus<br />　　extern "C" {<br />　　#endif<br />　　&lt;BR&gt;<br />　　int add（ int x, int y ）；<br />　　#ifdef __cplusplus<br />　　}<br />　　#endif<br />　　&lt;BR&gt;<br />　　#endif<br />　　则在 C 文件中 #include "cExample.h" 就不会报错了。<br />　　总之， 无论写 c 还是 c++头文件，只要是想以后混合编程，就最好加上<br />　　#ifdef __cplusplus<br />　　extern "C" {<br />　　#endif<br />　　/**** some declaration or so *****/<br />　　#ifdef __cplusplus<br />　　}<br />　　#endif /* end of __cplusplus */&lt;BR&gt;<br />　　错误的原因在于在C语言中没有关键字 extern "C"&lt;BR&gt;<br />　　（PS:有的人会单独使用extern "C" 来修饰某个函数，我觉得不是很好看，特别是在头文件中不能这么使用，因为在C文件中不能出现这样的关键字，所以最好使用最后介绍的条件编译 在一定条件下才使用 extern "C"）&lt;BR&gt;<br />　　&lt;P&gt;&lt;/P&gt;<br />　　&lt;PRE class=brush:java;&gt;&lt;PRE class=brush:java;&gt;&lt;/PRE&gt;<br />　　&lt;PRE class=brush:java;&gt;&lt;/PRE&gt;<br />　　&lt;PRE class=brush:java;&gt;&lt;/PRE&gt;<br />　　&lt;PRE class=brush:java;&gt;&lt;/PRE&gt;<br />　　&lt;PRE class=brush:java;&gt;&lt;/PRE&gt;<br />　　&lt;PRE class=brush:java;&gt;&lt;/PRE&gt;<br />　　&lt;PRE class=brush:java;&gt;&lt;/PRE&gt;<br />　　&lt;PRE class=brush:java;&gt;&lt;/PRE&gt;<br />　　&lt;PRE class=brush:java;&gt;&lt;/PRE&gt;<br />　　&lt;PRE class=brush:java;&gt;&lt;/PRE&gt; &lt;/PRE&gt;</p><img src ="http://www.cppblog.com/haosola/aggbug/206722.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/haosola/" target="_blank">HAOSOLA</a> 2014-04-26 13:45 <a href="http://www.cppblog.com/haosola/archive/2014/04/26/206722.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>printf()格式化输出详解</title><link>http://www.cppblog.com/haosola/archive/2014/04/08/206503.html</link><dc:creator>HAOSOLA</dc:creator><author>HAOSOLA</author><pubDate>Tue, 08 Apr 2014 08:26:00 GMT</pubDate><guid>http://www.cppblog.com/haosola/archive/2014/04/08/206503.html</guid><wfw:comment>http://www.cppblog.com/haosola/comments/206503.html</wfw:comment><comments>http://www.cppblog.com/haosola/archive/2014/04/08/206503.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/haosola/comments/commentRss/206503.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/haosola/services/trackbacks/206503.html</trackback:ping><description><![CDATA[<p>　　% - 0 m.n l或h 格式字符<br />　　下面对组成格式说明的各项加以说明：<br />　　&#9312;%:表示格式说明的起始符号，不可缺少。<br />　　&#9313;-:有-表示左对齐输出，如省略表示右对齐输出。<br />　　&#9314;0:有0表示指定空位填0,如省略表示指定空位不填。<br />　　&#9315;m.n:m指域宽，即对应的输出项在输出设备上所占的字符数。N指精度。用于说明输出的实型数的小数位数。对数值型的来说，未指定n时，隐含的精度为n=6位。<br />　　&#9316;l或h:l对整型指long型，对实型指double型。h用于将整型的格式字符修正为short型。<br />　　---------------------------------------<br />　　格式字符<br />　　格式字符用以指定输出项的数据类型和输出格式。<br />　　&#9312;d格式：用来输出十进制整数。有以下几种用法：<br />　　%d:按整型数据的实际长度输出。<br />　　%md:m为指定的输出字段的宽度。如果数据的位数小于m,则左端补以空格，若大于m,则按实际位数输出。<br />　　%ld:输出长整型数据。<br />　　&#9313;o格式：以无符号八进制形式输出整数。对长整型可以用"%lo"格式输出。同样也可以指定字段宽度用"%mo"格式输出。<br />　　例：<br />　　main（）<br />　　{ int a = -1;<br />　　printf（"%d, %o", a, a）；<br />　　}<br />　　运行结果：-1,177777<br />　　程序解析：-1在内存单元中（以补码形式存放）为（1111111111111111）2,转换为八进制数为（177777）8.<br />　　&#9314;x格式：以无符号十六进制形式输出整数。对长整型可以用"%lx"格式输出。同样也可以指定字段宽度用"%mx"格式输出。<br />　　&#9315;u格式：以无符号十进制形式输出整数。对长整型可以用"%lu"格式输出。同样也可以指定字段宽度用"%mu"格式输出。<br />　　&#9316;c格式：输出一个字符。<br />　　&#9317;s格式：用来输出一个串。有几中用法<br />　　%s:例如：printf（"%s", "CHINA"）输出"CHINA"字符串（不包括双引号）。<br />　　%ms:输出的字符串占m列，如字符串本身长度大于m,则突破获m的限制，将字符串全部输出。若串长小于m,则左补空格。<br />　　%-ms:如果串长小于m,则在m列范围内，字符串向左靠，右补空格。<br />　　%m.ns:输出占m列，但只取字符串中左端n个字符。这n个字符输出在m列的右侧，左补空格，注意：如果n未指定，默认为0.<br />　　%-m.ns:其中m、n含义同上，n个字符输出在m列范围的左侧，右补空格。如果n&gt;m,则自动取n值，即保证n个字符正常输出，注意：如果n未指定，默认为0.<br />　　如果是sprintf（desc, "%m.ns", sour）； 如果desc空间够的话，会在%m.ns 串 的结尾自动补null字符，不同于strncpy.<br />　　例如 :sprintf（desc, "%.3s", "123456"）； desc如果空间&gt;=4字节的话，第4个字节将是null字符。<br />　　&#9318;f格式：用来输出实数（包括单、双精度），以小数形式输出。有以下几种用法：<br />　　%f:不指定宽度，整数部分全部输出并输出6位小数。<br />　　%m.nf:输出共占m列，其中有n位小数，如数值宽度小于m左端补空格。<br />　　%-m.nf:输出共占n列，其中有n位小数，如数值宽度小于m右端补空格。<br />　　&#9319;e格式：以指数形式输出实数。可用以下形式：<br />　　%e:数字部分（又称尾数）输出6位小数，指数部分占5位或4位。<br />　　%m.ne和%-m.ne:m、n和"-"字符含义与前相同。此处n指数据的数字部分的小数位数，m表示整个输出数据所占的宽度。<br />　　&#9320;g格式：自动选f格式或e格式中较短的一种输出，且不输出无意义的零。<br />　　---------------------------------------<br />　　关于printf函数的进一步说明：<br />　　如果想输出字符"%",则应该在"格式控制"字符串中用连续两个%表示，如：<br />　　printf（"%f%%", 1.0/3）；<br />　　输出0.333333%.<br />　　---------------------------------------<br />　　对于单精度数，使用%f格式符输出时，仅前7位是有效数字，小数6位。<br />　　对于双精度数，使用%lf格式符输出时，前16位是有效数字，小数6位。<br />　　------------------------------------------------------------------<br />　　printf（）函数是格式输出函数，请求printf（）打印变量的指令取决与变量的类型。例如，在打印整数是使用%d符号，在打印字符是 用%c 符号。这些符号被称为转换说明。因为它们指定了如何不数据转换成可显示的形式。下列列出的是ANSI C标准printf（）提供的各种转换说明。<br />　　转换说明及作为结果的打印输出%a 浮点数、十六进制数字和p-记数法（C99）<br />　　%A 浮点数、十六进制数字和p-记法（C99）<br />　　%c 一个字符<br />　　%d 有符号十进制整数<br />　　%e 浮点数、e-记数法<br />　　%E 浮点数、E-记数法<br />　　%f 浮点数、十进制记数法<br />　　%g 根据数值不同自动选择%f或%e.<br />　　%G 根据数值不同自动选择%f或%e.<br />　　%i 有符号十进制数（与%d相同）<br />　　%o 无符号八进制整数<br />　　%p 指针<br />　　%s 字符串<br />　　%u 无符号十进制整数<br />　　%x 使用十六进制数字0f的无符号十六进制整数<br />　　%X 使用十六进制数字0f的无符号十六进制整数<br />　　%% 打印一个百分号<br />　　//还有一个特殊的格式%*.* ,这两个星号的值分别由第二个和第三个参数的值指定 printf（"%.*s \n", 8, "abcdefgggggg"）；<br />　　printf（"%*.*f \n", 3,3, 1.25456f）；使用printf （）函数 printf（）的基本形式： printf（"格式控制字符串",变量列表）；#include&lt;cstdio&gt;int main（）<br />　　{<br />　　//for int<br />　　int i=30122121;<br />　　long i2=309095024l;<br />　　short i3=30;<br />　　unsigned i4=2123453; printf（"%d,%o,%x,%X,%ld,%hd,%u\n",i,i,i,i,i2,i3,i4）；//如果是：%l,%h,则输不出结果<br />　　printf（"%d,%ld\n",i,i2）；//试验不出%ld和%d之间的差别，因为long是4bytes <strong><em><a href="http://www.yztrans.com/"><font color="#ffffec">www.yztrans.com</font></a></em></strong> <br />　　printf（"%hd,%hd\n\n\n",i,i3）；//试验了%hd和%d之间的差别，因为short是2bytes<br />　　//for string and char<br />　　char ch1='d';<br />　　unsigned char ch2=160;<br />　　char *str="Hello everyone!";<br />　　printf（"%c,%u,%s\n\n\n",ch1,ch2,str）；//unsigned char超过128的没有字符对应<br />　　//for float and double,unsigned and signed can not be used with double and float<br />　　float fl=2.566545445F;//or 2.566545445f<br />　　double dl=265.5651445; <strong><em><a href="http://www.jamo123.com/"><font color="#ffffec">www.jamo123.com</font></a></em></strong> <br />　　long double dl2=2.5654441454;<br />　　//%g没有e格式，默认6位包括小数点前面的数，<br />　　//%f没有e格式，默认6位仅只小数点后面包含6位<br />　　//%e采用e格式，默认6位为转化后的小数点后面的6位<br />　　printf（"%f,%e,%g,%.7f\n",fl,dl,dl,dl）；<br />　　printf（"%f,%E,%G,%f\n",fl,dl,dl,dl）；//%F is wrong<br />　　printf（"%.8f,%.10e\n",fl,dl）；<br />　　printf（"%.8e,%.10f\n\n\n",fl,dl）；<br />　　//for point<br />　　int *iP=&amp;i;<br />　　char *iP1=new char;<br />　　void *iP2;//dangerous!<br />　　printf（"%p,%p,%p\n\n\n",iP,iP1,iP2）；<br />　　//其他知识：负号，表示左对齐（默认是右对齐）；%6.3,6表示宽度，3表示精度<br />　　char *s="Hello world!";<br />　　printf（":%s: \n:%10s: \n:%.10s: \n:%-10s: \n:%.15s: \n:%-15s: \n:%15.10s: \n:%-15.10s:\n\n\n",<br />　　s,s,s,s,s,s,s,s）； double ddd=563.908556444;<br />　　printf（":%g: \n:%10g: \n:%.10g: \n:%-10g: \n:%.15g: \n:%-15g: \n:%15.10g: \n:%-15.10g:\n\n\n",<br />　　ddd,ddd,ddd,ddd,ddd,ddd,ddd,ddd）；<br />　　//还有一个特殊的格式%*.* ,这两个星号的值分别由第二个和第三个参数的值指定 printf（"%.*s \n", 8, "abcdefgggggg"）；<br />　　printf（"%*.*f \n", 3,3, 1.25456f）； return 0;<br />　　}</p><img src ="http://www.cppblog.com/haosola/aggbug/206503.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/haosola/" target="_blank">HAOSOLA</a> 2014-04-08 16:26 <a href="http://www.cppblog.com/haosola/archive/2014/04/08/206503.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>