<?xml version="1.0" encoding="utf-8"?><rss version="2.0">
<channel>
<title><![CDATA[Elegant&nbsp;C++]]></title>
<link>http://blog.pfan.cn/aurora</link>
<description>编程爱好者博客</description>
<language>zh-cn</language>
			<item>
		<title><![CDATA[强制类型转换的错误]]></title>
		<link>http://blog.pfan.cn/aurora/53331.html</link>
		<description><![CDATA[今天遇到一个虚拟继承引起的类型转换错误，网上查到相关资料记录如下：
   class VirtualBase
   {
      public:
         virtual class Derived* asDerived() = 0;
   };
   
   class Derived : virtual public VirtualBase
   {
      public:
         virtual Derived* asDerived();
   };
   
   Derived*
   Derived::asDerived()
   {
      return this;
   }
   
   void
   main()
   {
      Derived d;
      Derived* dp = 0;
      VirtualBase* vp = (VirtualBase*)&amp;d;
   
      dp = (Derived*)vp; // ERROR! Cast from virtual base class pointer      &nbsp;      dp = vp-&gt;asDerived(); // OK! Cast in function asDerived
   }
&nbsp; &nbsp;Virtual base classes give rise to other type conversion problems. It is possible to convert a pointer, to an instance of a class which has a virtual base class, to a pointer to an object of that virtual base class. The opposite conversion is not allowed, i.e. the type conversion is not reversible. For this reason, we do not recommend the conversion of a derived class poi]]></description>
		<author><![CDATA[emmett]]></author>
		<pubDate>2012-03-02 15:09:00</pubDate>
		</item>
				<item>
		<title><![CDATA[const引用和非const引用的一个区别]]></title>
		<link>http://blog.pfan.cn/aurora/52528.html</link>
		<description><![CDATA[今天在一个网站上(http://www.cppblog.com/kongque/archive/2010/12/20/137036.html)看到一篇文章，记录下来。
原帖：
各位请看下面的代码
&nbsp;1&nbsp;struct&nbsp;A&nbsp;{};
&nbsp;2&nbsp;
&nbsp;3&nbsp;struct&nbsp;B
&nbsp;4&nbsp;{
&nbsp;5&nbsp;public:
&nbsp;6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;B(){}
&nbsp;7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;B(A&amp;&nbsp;a){}
&nbsp;8&nbsp;};
&nbsp;9&nbsp;
10&nbsp;struct&nbsp;B1&nbsp;:&nbsp;public&nbsp;B
11&nbsp;{
12&nbsp;public:
13&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;B1(const&nbsp;B&amp;&nbsp;b)&nbsp;:&nbsp;B(b)
14&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{}
15&nbsp;};
16&nbsp;
17&nbsp;int&nbsp;main()
18&nbsp;{
19&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;a;
20&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;B1&nbsp;b1(a);
21&nbsp;
22&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0;
23&nbsp;}


上面的代码可以编译通过（gcc和vs2005下均测试通过），但是如果我将第13行的const修饰符去掉，编译就不能通过，报错说没有匹配的函数，请问各位高手这是为什么？望指教。


评论
#&nbsp;re: C++的一个疑惑&nbsp;2010-12-20 22:04&nbsp;keane&nbsp;
非const的引用参数只能是相同类型，const的引用参数可以传相关类型的参数进来


&nbsp;&nbsp;回复&nbsp;&nbsp;更多评]]></description>
		<author><![CDATA[emmett]]></author>
		<pubDate>2011-05-07 13:43:00</pubDate>
		</item>
				<item>
		<title><![CDATA[Intel体系MMX指令&amp;指令说明]]></title>
		<link>http://blog.pfan.cn/aurora/52526.html</link>
		<description><![CDATA[EMMS

MMX状态置空:
将FP特征字置空(全1),使后续浮点指令可以使用浮点寄存器,其他MMX指令自动置FP为全0.本指令应在所有MMX例程结束和调用可含有FP指令的例程时使用,以清除MMX状态.

MOVD mm,r/m32
MOVD r/m32,mm
转移32位数据:
将32位数据从整型寄存器/内存移到MMX寄存器,和反向移动.MOVD不能在MMX寄存器之间,内存之间及整型寄存器之间移动数据.目标操作数为MMX寄存器时,32位源操作数写入目标寄存器的低32位.目标寄存器"0扩展"为64位.源操作数为MMX寄存器时,该寄存器的低32位被写入目标操作数.

MOVQ mm,r/m64
MOVQ r.m64,mm
转移64位数据:
将64位数据从整型寄存器/内存移到MMX寄存器,和反向移动.目标操作数和源操作数可为MMX寄存器,64位内存操作数.但MOVQ不能在内存和内存之间进行数据转移.

PACKSSWB mm,mm/m64
PACKSSDW mm,mm/m64

有符号饱和方式数据成组:
将MMX寄存器和MMX寄存器/内存单元中的有符号字组变成MMX寄存器的有符号字节组.和将MMX寄存器和MMX寄存器/内存单元中的有符号双字组变成MMX寄存器的有符号字组.(注1)

PACKUSWB mm,mm/m64
无符号饱和方式数据成组
将MMX寄存器和MMX寄存器/内存单元中的有符号字组变成MMX寄存器的无符号字节组.(注1)

PADDB mm,mm/m64
PADDW mm,mm/m64
PADDD mm,mm/m64

环绕方式数据组相加:
按环绕方式将MMX寄存器/内存单元中的字节组(字组,双字组)相加到MMX寄存器中(注1)

PADDSB mm,mm/m64
PADDSW mm,mm/m64

饱和方式有符号数据组相加:
按饱和方式将MMX寄存器/内存单元中的有符号字节组(字组)相加到MMX寄存器中的有符号字节组(字组)数据.(注1)

PADDUSB mm,mm/m64
PADDUSW mm,mm/m64

饱和方式无符号数据组相加:
按饱和方式将MMX寄存器/内存单元中的无符号字节组(字组)相加到MMX寄存器中的无符号字节组(字组)数据.(注1)

PAND m]]></description>
		<author><![CDATA[emmett]]></author>
		<pubDate>2011-05-06 23:34:00</pubDate>
		</item>
				<item>
		<title><![CDATA[函数的调用约定]]></title>
		<link>http://blog.pfan.cn/aurora/52087.html</link>
		<description><![CDATA[Using Win32 calling conventions 
原文在这里：http://www.unixwiz.net/techtips/win32-callconv.html#decor
下面只是为了防止上面网页打不开而做的简单拷贝，文字未经排版，看起来很不舒服。请尽量使用以上链接查看
&nbsp;


When writing code for the Win32 platform, most developers don't pay attention to selecting a "calling convention", and in most cases it doesn't really matter much. But as systems get larger and split into more modules (especially when third-party modules are to be included), this becomes something that cannot really be ignored. 
&nbsp;
In this Tech Tip, we discuss what the MSVC calling conventions are, why to choose one over the other, and "big system" considerations that may not be obvious. 

Calling Conventions 
Traditionally, C function calls are made with the caller pushing some parameters onto the stack, calling the function, and then popping the stack to clean up those pushed arguments. 
/* example of __cdecl */
push arg1
push arg2
push arg3
call function
add  sp,12        // effectively "pop; pop; pop" It t]]></description>
		<author><![CDATA[emmett]]></author>
		<pubDate>2010-12-10 12:07:00</pubDate>
		</item>
				<item>
		<title><![CDATA[C++标准学习之数组的初始化]]></title>
		<link>http://blog.pfan.cn/aurora/51936.html</link>
		<description><![CDATA[实际应用中我们在定义一个数组的时候常常希望他能按照我们的想像初始化，比如：
char buf1[10] = {0};
这样写的目的我想大部分都是期望编译器会为每个元素自动初始化为0。测试一下发现基本上所有编译器也都是这么做的，但这么做“标准”吗？ 或者说可靠吗？ 
看看标准中是怎么说的吧：
8.5.1 Aggregates
7.&nbsp; If there are fewer initializers in the list than there are members in the aggregate, then each&nbsp;&nbsp; member not explicitly initialized shall be value-initialized (8.5). 
[Example:
struct S { int a; char* b; int c; };
S ss = { 1, "asdf" };
initializes ss.a with 1, ss.b with "asdf", and ss.c with the value of an expression of the form
int(), that is, 0. ]
上面提到的 value-initialized 在8.5节给出了定义：
To value-initialize an object of type T means:
— if T is a class type (clause 9) with a user-declared constructor (12.1), then the default constructor&nbsp;for T is called (and the initialization is ill-formed if T has no accessible default constructor);
— if T is a non-union class type without a user-declared constructor, then every non-static data member and base-class component of T is value-initialized;
— if]]></description>
		<author><![CDATA[emmett]]></author>
		<pubDate>2010-10-26 12:05:00</pubDate>
		</item>
				<item>
		<title><![CDATA[字符串内部查找函数]]></title>
		<link>http://blog.pfan.cn/aurora/40609.html</link>
		<description><![CDATA[char* strchr( const char* str, int chr );在str中查找第一次出现的chr，没找到则返回0
char* strrchr( const char* str, int chr );在str中查找最后一次出现的chr，没找到则返回0
char* strstr( const char* str, const char* substr );在str中查找第一次出现的substr，没找到则返回0
size_t strspn( const char* str, const char* chrset );在str中查找第一个不包含在chrset中的字符的索引，即查找从str第一个字符开始的只包含chrset中字符的最长字符串字符数目&nbsp;strspn( "cabbage", "abc" ) == 5;&nbsp;strspn( "cabbage", "dc" ) == 1;&nbsp;strspn( "cabbage", "d" ) == 0;&nbsp;strspn("cabbage", "a") == 0;&nbsp;strspn( "cabbage", "" ) == 0;&nbsp;strspn( "cabbage", "\0" ) == 0;&nbsp;strspn("cabbage", "abceg" ) == 7;//返回str长度
char* strspnp( const char* str, const char* chrset );和strspn功能一样，只是返回指针const char* str = "cabbage"strspnp( str, "abc" ) == str+5;strspnp( str, "dc" ) == str+1;strspnp( str, "d" ) == str+0;strspnp( str, "abceg" ) == 0; // 和strspn不一样
size_t strcspn( const char* str, const char* chrset );在str中查找第一次出现在chrset中的字符位置，没找到则返回str结尾的'\0'的位置strcspn( "xyzbxz", "abc" ) = 3strcspn( "xyzbxz", "xyz" ) = 0strcsp]]></description>
		<author><![CDATA[emmett]]></author>
		<pubDate>2009-01-20 10:36:00</pubDate>
		</item>
				<item>
		<title><![CDATA[虚函数原理的一个小例子]]></title>
		<link>http://blog.pfan.cn/aurora/39500.html</link>
		<description><![CDATA[class A { &nbsp; &nbsp; virtual void fun(){printf("this is a's private fun\n");} }; class B { public: &nbsp; &nbsp; virtual void fun(){printf("this is b's public fun\n");} }; int _tmain(int argc, _TCHAR* argv[]) { &nbsp; &nbsp; A* pa = new A; &nbsp; &nbsp; B* pb=(B*)pa; &nbsp; &nbsp; pb-&gt;fun(); &nbsp; &nbsp; return 0; } 
对于pb-&gt;fun()编译器首先检查pb的类型为B，于是到B的定义里找fun发现是virtual。于是做了如下转换：
typedef void (*pFoo)(); 
pFoo(*(int*)(*((int*)pb)))();
这样起始调用的是A中的fun。
如果B的fun为虚，而A的fun非虚，编译器同样会做上面的转换，当然运行的时候就报内存访问违规了。原因很简单，A中并没有虚函数，自然不会有虚表。
如果B的fun是非virtual，编译器就编译器就直接调用了B中的fun：
B::fun(); 这种调用最简单，因为不涉及虚函数，自然不会做类似上面的转换。]]></description>
		<author><![CDATA[emmett]]></author>
		<pubDate>2008-11-20 18:12:00</pubDate>
		</item>
				<item>
		<title><![CDATA[如何取得结构变量的偏移地址]]></title>
		<link>http://blog.pfan.cn/aurora/39467.html</link>
		<description><![CDATA[struct Foo{
&nbsp;&nbsp; int a;
&nbsp;&nbsp; char b;
&nbsp;&nbsp; short c;
};
如果我想观察编译器为了达到内存对齐的要求都在Foo的每个变量后加了多少padding字节，有什么办法？
我们知道编译器在定位一个成员变量的时候利用了2个信息：
1. 类对象的地址
2.将要访问的成员变量相对于类起始地址的偏移地址
这样编译器就可以通过 类对象的地址+成员变量的偏移地址 的简单计算得到成员变量在内存中的真正地址。
回到我们先头的问题，其实我们就是想知道第2个信息，即：将要访问的成员变量相对于类起始地址的偏移地址。
偏移地址的计算是编译器在编译该类的时候就已经确定的，因此应该有办法在编译期就得到这个值。考虑一般的调用情况：
Foo foo;
Foo *pfoo = &amp;foo;
pfoo-&gt;b;&nbsp; //这一句编译器就将计算a的真正地址=pfoo的值+b的偏移地址。
那么如果pfoo的值为零，我们岂不是正好得到a的偏移地址吗？也就是
((Foo*)0)-&gt;b我们能通过这样的形式获得我们想要的偏移地址吗？
OK，运行一下... ...
发现运行期报了一个内存访问违规的异常（VC8），正常情况下，如果你的编译器没有什么问题，你都应该得到这个异常。仔细观察这个异常：
Unhandled exception at 0x00411a06 in sss.exe: 0xC0000005: Access violation reading location 0x00000004.
啊？0x00000004不就是我们想要的偏移地址吗？ 
恍惚一下~~&nbsp; 
发现自己犯了一个弱智的错误：我们不是要访问b的值，我们只是想得到他的偏移地址而已。把他的偏移地址当做他的内存地址当然会出错啦~~毕竟你的pfoo什么都不是（空指针）。
于是乎强制转换成地址：(int)((void*)(&amp;(((Foo*)0)-&gt;c))) 
这次输出正确了，就是4.
其实在stddef.h中有更加完整的定义，就是offsetof宏：
#define offsetof(s,m)&nbsp;&nbsp; (size_t)&amp;reinterpret_ca]]></description>
		<author><![CDATA[emmett]]></author>
		<pubDate>2008-11-19 10:42:00</pubDate>
		</item>
				<item>
		<title><![CDATA[视频与图像RGB/YUV格式详解]]></title>
		<link>http://blog.pfan.cn/aurora/38842.html</link>
		<description><![CDATA[计算机彩色显示器显示色彩的原理与彩色电视机一样，都是采用R（Red）、G（Green）、B（Blue）相加混色的原理：通过发射出三种不同强度的电子束，使屏幕内侧覆盖的红、绿、蓝磷光材料发光而产生色彩。这种色彩的表示方法称为RGB色彩空间表示（它也是多媒体计算机技术中用得最多的一种色彩空间表示方法）。
根据三基色原理，任意一种色光F都可以用不同分量的R、G、B三色相加混合而成。
&nbsp;
F = r [ R ] + g [ G ] + b [ B ]
&nbsp;
其中，r、g、b分别为三基色参与混合的系数。当三基色分量都为0（最弱）时混合为黑色光；而当三基色分量都为k（最强）时混合为白色光。调整r、g、b三个系数的值，可以混合出介于黑色光和白色光之间的各种各样的色光。
那么YUV又从何而来呢？在现代彩色电视系统中，通常采用三管彩色摄像机或彩色CCD摄像机进行摄像，然后把摄得的彩色图像信号经分色、分别放大校正后得到RGB，再经过矩阵变换电路得到亮度信号Y和两个色差信号R－Y（即U）、B－Y（即V），最后发送端将亮度和色差三个信号分别进行编码，用同一信道发送出去。这种色彩的表示方法就是所谓的YUV色彩空间表示。
采用YUV色彩空间的重要性是它的亮度信号Y和色度信号U、V是分离的。如果只有Y信号分量而没有U、V分量，那么这样表示的图像就是黑白灰度图像。彩色电视采用YUV空间正是为了用亮度信号Y解决彩色电视机与黑白电视机的兼容问题，使黑白电视机也能接收彩色电视信号。
YUV与RGB相互转换的公式如下（RGB取值范围均为0-255）：
&nbsp;
Y = 0.299R + 0.587G + 0.114B
U = -0.147R - 0.289G + 0.436B
V = 0.615R - 0.515G - 0.100B
&nbsp;
R = Y + 1.14V
G = Y - 0.39U - 0.58V
B = Y + 2.03U
&nbsp;
在DirectShow中，常见的RGB格式有RGB1、RGB4、RGB8、RGB565、RGB555、RGB24、RGB32、ARGB32等；常见的YUV格式有YUY2、YUYV、YVYU、UYVY、AYUV、Y41P、Y411、Y211、IF09、IYUV、YV12、YVU9、YUV411、]]></description>
		<author><![CDATA[emmett]]></author>
		<pubDate>2008-10-12 14:05:00</pubDate>
		</item>
				<item>
		<title><![CDATA[浮点数的存储格式]]></title>
		<link>http://blog.pfan.cn/aurora/36314.html</link>
		<description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;基于IEEE 754的浮点数存储格式&nbsp;&nbsp;&nbsp; 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IEEE（Institute of Electrical and Electronics Engineers，电子电气工程师协会）在I985年制定的IEEE 754（IEEE Standard for Binary Floating-Point Arithmetic, ANSI/IEEE Std 754-1985 ）二进制浮点运算规范，是浮点运算部件事实上的工业标准。
1&nbsp; 浮点数
&nbsp;&nbsp;&nbsp; 在计算机系统的发展过程中，曾经提出过多种方法表示实数，但是到目前为止使用最广泛的是浮点表示法。相对于定点数而言，浮点数利用指数使小数点的位置可以根据需要而上下浮动，从而可以灵活地表达更大范围的实数。
&nbsp;&nbsp;&nbsp; 浮点数表示法利用科学计数法来表达实数。通常，将浮点数表示为 ± d.dd…d ×βe，其中d.dd… d 称为有效数字（significand），它具有 p 个数字（称p位有效数字精度），β为基数（Base），e为指数（Exponent），±表示实数的正负[1,2]。更精确地，± d0.d１d２…dp－1 × βe， 表示以下数
&nbsp;±（d0＋d1β－1＋… ＋dp－1β－（p－1））βe，（0≤di＜β）。
&nbsp;&nbsp;&nbsp; 对实数的浮点表示仅作如上的规定是不够的，因为同一实数的浮点表示还不是唯一]]></description>
		<author><![CDATA[emmett]]></author>
		<pubDate>2008-06-27 11:59:00</pubDate>
		</item>
				<item>
		<title><![CDATA[另类的成员函数调用]]></title>
		<link>http://blog.pfan.cn/aurora/36175.html</link>
		<description><![CDATA[对于给定的一个类：
class Foo{public:&nbsp;&nbsp; void CALLINGCONVENTION showMe( int _d ){&nbsp;&nbsp; //__cdecl or __stdcall is needed.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cout &lt;&lt; "Foo::showMe&nbsp; _d = " &lt;&lt; _d &lt;&lt; endl;&nbsp;&nbsp; }};
通常我们会通过下面的方式进行调用该类中的成员方法：
Foo foo;
foo.showMe(100);
或者通过成员函数指针的形式：
void (Foo::*pfn)(int) = &amp;Foo::showMe;
(foo.*pfn)(100);
我总是认为这中使用成员函数指针的调用方式（或是语法）感到很奇怪，不过它毕竟是标准的并且能够为C++编译器认可的调用方式。几乎在所有编译器都不允许将成员函数的地址直接赋给其他类型的变量（如DWORD等，即使使用reinterpret_cast也无济于事）例如：而只能将其赋给给与该成员函数类型声明（包括所使用的调用约定，返回值，函数参数）完全相同的变量。因为成员函数的声明中都有一个隐藏的this指针，该指针会通过ECX或栈的方式传递到成员函数中，为了成员函数被安全调用，编译器禁止此类型的转换也在情理当中。但有时我们为了实现特殊的目的需要将成员函数的地址直接赋给一个DWORD变量（或其他任何能够保存一个指针值的变量，通常只要是一个32位的变量即可），我们可以通过下面两种方法来实现：下面的这几种试图将一个成员函数的地址保存到一个DWORD中都将被编译器认为是语法错误：DWORD dwFooAddrPtr= 0;
dwFooAddrPtr = (DWORD) &amp;Foo::showMe; /* Error C2440 */
dwFooAddrPtr = reinterpret_cast&lt;DWORD&gt; (&amp;Foo::showMe); /* Error C2440 */因为成员函数的声明中都有一个隐藏的this指针，该指针会通过ECX或栈的方式传递到成员函数中，为了成员]]></description>
		<author><![CDATA[emmett]]></author>
		<pubDate>2008-06-17 11:38:00</pubDate>
		</item>
				<item>
		<title><![CDATA[&quot;Debugger&nbsp;detected-please&nbsp;close&nbsp;it&nbsp;...&quot;]]></title>
		<link>http://blog.pfan.cn/aurora/36153.html</link>
		<description><![CDATA[转自：http://www.directshow.cn/forum/viewthread.php?tid=3370&amp;pid=10459&amp;page=1&amp;extra=page%3D2#pid10459&nbsp;感谢发帖人！
"Debugger detected - please close it down and restart!"问题的解决
Debugger detected&nbsp;&nbsp;- please close it down and restart!...blahblahblah这个问题有一些人遇到过，我也曾经遇到过，可是没有解决，直到这一次......还是从头说起，还是GMFBridge项目，我用GMFPlay这个例子来研究GMFBridge，很快编译通过之后，我开始测试例子的功能，单独用编译出来的可执行文件可以正常执行，于是接下来很自然我认为项目已经没有问题了，开始在VS2005的IDE下执行，咦，怎么添加完文件之后就不动了，程序无响应，同时听到“邦”的一声，这是哪里出错了？还弹出了对话框？可是看不到对话框啊，这是怎么回事？折腾了半天，只是确定是在GMFBridge的Bridge.cpp 566行会出问题，就是下面这行代码：&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;hr = pGraph-&gt;Render(pPin);进一步发现是在循环第二次的时候出问题，这个时候有点挠脑袋了，为什么会在Render的时候出错呢？想跟踪也没法跟踪了。搁了一夜，第二天想起了干脆在出错的时候dump出来分析，反正也是学习，多花点时间研究一下就是了。于是用DebugDiag（https://www.microsoft.com/downlo ... splaylang=en&amp;tm）在出错的时候dump了一个full dump和mini dump出来，用DebugDiag分析，DebugDiag真是不错，太好用了，提示“Detected possible blocking or leaked critical section at ntdll!LdrpLoaderLock owned by thread 5......”“The entry-point function for a dynami]]></description>
		<author><![CDATA[emmett]]></author>
		<pubDate>2008-06-16 10:50:00</pubDate>
		</item>
				<item>
		<title><![CDATA[“条件递进式编程”技巧两例]]></title>
		<link>http://blog.pfan.cn/aurora/35863.html</link>
		<description><![CDATA[转自：http://blog.csdn.net/happydeer/archive/2004/08/06/66743.aspx#785737
什么是“条件递进式编程”？呵呵，这是我自己起的名字。所谓条件递进式编程，就是指需要一系列相关函数组合执行的情况下，后续函数执行的必要性由前面所有函数执行是否成功来决定。比如说，有一系列函数如下DoSomething1、DoSomething2、DoSomething3......DoSomethingN；DoSomething2函数执行的前提是DoSomething1函数必须执行成功，DoSomething3函数执行的前提是DoSomething1和DoSomething2都执行成功，DoSomethingN函数执行的前提是DoSomething1、DoSomething2、DoSomething3......DoSomething(N-1)都执行成功。
你会如何来写这段程序？不假思索，你可能会如下编写：
BOOL DoSomething(){&nbsp;if (DoSomething1())&nbsp;{&nbsp;&nbsp;if (DoSomething2())&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;if (DoSomething3())&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;// ...&nbsp;&nbsp;&nbsp;&nbsp;return TRUE;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;}&nbsp;}&nbsp;return FALSE;}
这种层层嵌套的结构，逻辑上并没有错，但代码的可读性就差了一点。如果系列函数的数量很多（即上述N的值很大），那么整个代码结构的宽度将变得很长，代码的可读性将进一步降低，日后修改代码的时候也很容易出错。
下面，我将给出两种我本人认为很不错的程序结构。这两种程序结构不是我自创的，但我能看到它们智慧的光芒。另外，关于以下两种程序结构，我不想给出过多解释，读者用心慢慢体会吧！
程序结构1：
BOOL DoSomething(){&nbsp;BOOL pass = DoSomething1();&nbsp;if (pass)&nbsp;{&nbsp;&nbsp;pass = Do]]></description>
		<author><![CDATA[emmett]]></author>
		<pubDate>2008-06-03 17:02:00</pubDate>
		</item>
				<item>
		<title><![CDATA[学习心得]]></title>
		<link>http://blog.pfan.cn/aurora/35325.html</link>
		<description><![CDATA[此帖记录对自己学习有帮助的一些tips
1. 温故而知新
&nbsp;&nbsp;&nbsp; 这是一句老话，回过头来想想自己走过的路，才发现这四个字价值千金。 经常总结一下，不时的回头翻翻自己的笔记，你会发现之前的想法可能有些单纯甚至错误，毕竟你现在在从一个更高的角度去看问题。在纠正和完善之中你在成长。
2.不要积攒问题
&nbsp;&nbsp;&nbsp; 问题在刚发现的时候对它的理解总是最直接，最能接近你内心困惑的。这是个解决它的好时机。如果累积问题，慢慢的对这个问题的最初困惑会越来越不清楚，这会消磨你解决问题的热情。]]></description>
		<author><![CDATA[emmett]]></author>
		<pubDate>2008-05-22 09:43:00</pubDate>
		</item>
				<item>
		<title><![CDATA[对受保护成员的一个额外访问控制]]></title>
		<link>http://blog.pfan.cn/aurora/34999.html</link>
		<description><![CDATA[对于受保护的成员变量，子类对其的访问有一个额外的限制：
Clause 11.5:
When a friend or a member function of a derived class references a protected nonstatic member function or protected nonstatic data member of a base class, an access check applies in addition to those described earlier in clause 11.102) Except when forming a pointer to member (5.3.1), the access must be through a pointer to, reference to, or object of the derived class itself (or any class derived from that class) (5.2.5). If the access is to form a pointer to member, the nested-name-specifier shall name the derived class (or any class derived from that class).
例子：
class Base{
protected:
&nbsp;&nbsp;&nbsp;&nbsp; Base(int){}
&nbsp;&nbsp;&nbsp;&nbsp; int i;
};
class Derive:public Base{
public:
&nbsp;&nbsp;&nbsp;&nbsp; Derive():Base(100){&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;//OK
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Base b(100);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbs]]></description>
		<author><![CDATA[emmett]]></author>
		<pubDate>2008-05-13 11:42:00</pubDate>
		</item>
				<item>
		<title><![CDATA[能在构造函数中调用虚函数吗？]]></title>
		<link>http://blog.pfan.cn/aurora/33981.html</link>
		<description><![CDATA[可以，但是你要小心，因为在构造函数里虚函数的机制并没有启用。
#include&lt;string&gt;	#include&lt;iostream&gt;	using namespace std;	class B {	public:		B(const string&amp; ss) { cout &lt;&lt; "B constructor\n"; f(ss); }		virtual void f(const string&amp;) { cout &lt;&lt; "B::f\n";}	};	class D : public B {	public:		D(const string &amp; ss) :B(ss) { cout &lt;&lt; "D constructor\n";}		void f(const string&amp; ss) { cout &lt;&lt; "D::f\n"; s = ss; }	private:		string s;	};	int main()	{		D d("Hello");	}the program compiles and produce 	B constructor
	B::f
	D constructor注意：输出的是B::f,而不是D::f，因为在构建D的时候会先构建B，在B尚未完成构造的时候D是不存在的，因此如果在B中调用了还没有构建的D的成员函数（可能会修改D的变量），会导致错误。
在析构函数中同样的道理，只有本类的函数会被调用，以避免调用已经被释放的派生类的成员函数。]]></description>
		<author><![CDATA[emmett]]></author>
		<pubDate>2008-04-07 18:08:00</pubDate>
		</item>
				<item>
		<title><![CDATA[C++为什么没有让delete后的指针自动清零？]]></title>
		<link>http://blog.pfan.cn/aurora/33976.html</link>
		<description><![CDATA[大家都知道连续两次对同一个指针delete ，会造成严重的错误（一般的编译器会检测出这样的错误）。
于是很多人这样写delete：
#define SAFE_DELETE((p)&nbsp; delete (p); p = 0;
这样就可以有效地避免类似的错误，因为对零指针delete是没有害处的。可是C++为什么不支持自动对delete后的指针清零呢？
原因是,delete操作符并没有要求它的操作数必须是个左值，所以p=0这样的操作未必合法。
其次，即使C++实现了这样的机制，它也可能形同虚设，比如：
class Foo;
Foo *pfoo1 = new Foo();
Foo *pfoo2 = pfoo1;
delete pfoo1;
delete pfoo2;
最后一句一样会出同样的错。
其实想实现SAFE_DELET这样的功能有个更安全可靠的办法：
template&lt;typename T&gt; inline void Destroy( T *&amp;p){ delete p; p = 0;}
如此以来，便可以更安全的实现指针的释放。可这样也有一个明显的缺点，
正像它只能接受左值一样，Destroy将无法接受非左值对象：
Destroy( pfoo1 );
Destroy( pfoo1+1 ); //错误(参数非左值),但delete pfoo1+1;在C++中是正确的.
所以并没有好的方法解决重复释放这样的问题，只能靠程序员的细心了。
&nbsp;]]></description>
		<author><![CDATA[emmett]]></author>
		<pubDate>2008-04-07 16:32:00</pubDate>
		</item>
				<item>
		<title><![CDATA[异常处理ABC]]></title>
		<link>http://blog.pfan.cn/aurora/33888.html</link>
		<description><![CDATA[此文记录我对异常的一点点基本了解。欢迎大家批评指正。
http://www.microsoft.com/msj/0197/exception/exception.aspx
http://blogs.msdn.com/cbrumme/archive/2003/10/01/51524.aspx
零: c++异常用在哪里？
&nbsp;&nbsp;&nbsp;&nbsp; In C++, exceptions is used to signal errors that cannot be handled locally, such as the failure to acquire a resource in a constructor. For example: 	class Vector {
		int sz;
		int* elem;
		class Range_error { };
	public:
		Vector(int s) : sz(s) { if (sz&lt;0) throw Range_error(); /* ... */ }
		// ...
	};

Do not use exceptions as simply another way to return a value from a function. Most users assume - as the language definition encourages them to - that exception-handling code is error-handling code, and implementations are optimized to reflect that assumption. 
A key technique is resource acquisiton is initialization (sometimes abbreviated to RAII), which uses classes with destructors to impose order on resource management. For example: 	void fct(string s)
	{
		File_handle f(s,"r");	//]]></description>
		<author><![CDATA[emmett]]></author>
		<pubDate>2008-04-03 11:08:00</pubDate>
		</item>
				<item>
		<title><![CDATA[标准C++获取文件长度的方法]]></title>
		<link>http://blog.pfan.cn/aurora/33823.html</link>
		<description><![CDATA[转自http://groups.google.com/group/bruceteen/browse_thread/thread/90ef833dfbad9ab6
#include &lt;fstream&gt; #include &lt;cstdio&gt; using namespace std; 
int main( void ) { &nbsp; &nbsp; fstream file( "D:\\张国荣.JPG", ios_base::binary | ios_base::in ); &nbsp; &nbsp; if( file.is_open() ) &nbsp; &nbsp; { &nbsp; &nbsp; &nbsp; &nbsp; // ... // 这里可以进行其它操作 &nbsp; &nbsp; &nbsp; &nbsp; fstream::pos_type cur_pos = file.tellg(); &nbsp; &nbsp; &nbsp; &nbsp; file.seekg( 0L, ios::end ); &nbsp; &nbsp; &nbsp; &nbsp; fstream::pos_type end_pos = file.tellg(); &nbsp; &nbsp; &nbsp; &nbsp; file.seekg( cur_pos, ios::beg ); &nbsp; &nbsp; &nbsp; &nbsp; streamoff flen = end_pos; // 注1 &nbsp; &nbsp; &nbsp; &nbsp; printf( "%I64d\n", flen ); &nbsp; &nbsp; } &nbsp; &nbsp; return 0; 
} 
// 注1: 在gcc3.4.2中 streamoff 是 int64_t. // &nbsp; &nbsp; &nbsp;而在VC++6.0中 streamoff 是 long, 所以除了标准的 // &nbsp; &nbsp; &nbsp;operator fposstreamoff() const 外, 又提供了一个 // &nbsp; &nbsp; &nbsp;fpos_t get_fpos_t() const, fpos_t 是 __int64.]]></description>
		<author><![CDATA[emmett]]></author>
		<pubDate>2008-04-02 11:26:00</pubDate>
		</item>
				<item>
		<title><![CDATA[Why&nbsp;Java&nbsp;Will&nbsp;Always&nbsp;Be&nbsp;Slower&nbsp;than&nbsp;C++]]></title>
		<link>http://blog.pfan.cn/aurora/33265.html</link>
		<description><![CDATA[Why&nbsp;Java&nbsp;Will&nbsp;Always&nbsp;Be&nbsp;Slower&nbsp;than&nbsp;C++by&nbsp;Dejan&nbsp;Jelovic&nbsp;"Java&nbsp;is&nbsp;high&nbsp;performance.&nbsp;By&nbsp;high&nbsp;performance&nbsp;we&nbsp;mean&nbsp;adequate.&nbsp;By&nbsp;adequate&nbsp;we&nbsp;mean&nbsp;slow."&nbsp;-&nbsp;Mr.&nbsp;BunnyAnybody&nbsp;that&nbsp;has&nbsp;ever&nbsp;used&nbsp;a&nbsp;non-trivial&nbsp;Java&nbsp;program&nbsp;or&nbsp;has&nbsp;programmed&nbsp;in&nbsp;Java&nbsp;knows&nbsp;that&nbsp;Java&nbsp;is&nbsp;slower&nbsp;than&nbsp;native&nbsp;programs&nbsp;written&nbsp;in&nbsp;C++.&nbsp;This&nbsp;is&nbsp;a&nbsp;fact&nbsp;of&nbsp;life,&nbsp;something&nbsp;that&nbsp;we&nbsp;accept&nbsp;when&nbsp;we&nbsp;use&nbsp;Java.However,&nbsp;many&nbsp;folks&nbsp;would&nbsp;like&nbsp;to&nbsp;convince&nbsp;us&nbsp;that&nbsp;this&nbsp;is&nbsp;just&nbsp;a&nbsp;temporary&nbsp;condition.&nbsp;Java&nbsp;is&nbsp;not&nbsp;slow&nbsp;by&nbsp;design,&nbsp;they&nbsp;say.&nbsp;Instead,&nbsp;it&nbsp;is&nbsp;slow&nbsp;because&nbsp;today's&nbsp;J]]></description>
		<author><![CDATA[emmett]]></author>
		<pubDate>2008-03-09 22:22:00</pubDate>
		</item>
		</channel>
</rss>