﻿<?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++博客-MyMSDN-随笔分类-知识库(KnowledgeLibrary)</title><link>http://www.cppblog.com/mymsdn/category/3173.html</link><description>MyMSDN记录开发新知道</description><language>zh-cn</language><lastBuildDate>Wed, 07 Apr 2010 20:07:21 GMT</lastBuildDate><pubDate>Wed, 07 Apr 2010 20:07:21 GMT</pubDate><ttl>60</ttl><item><title>[翻译]高效使用auto_ptr</title><link>http://www.cppblog.com/mymsdn/archive/2010/04/07/Using-auto_ptr-Effectively.html</link><dc:creator>volnet</dc:creator><author>volnet</author><pubDate>Wed, 07 Apr 2010 11:08:00 GMT</pubDate><guid>http://www.cppblog.com/mymsdn/archive/2010/04/07/Using-auto_ptr-Effectively.html</guid><wfw:comment>http://www.cppblog.com/mymsdn/comments/111882.html</wfw:comment><comments>http://www.cppblog.com/mymsdn/archive/2010/04/07/Using-auto_ptr-Effectively.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/mymsdn/comments/commentRss/111882.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mymsdn/services/trackbacks/111882.html</trackback:ping><description><![CDATA[<h4><i>本文来自C/C++用户日志，17（10），1999年10月</i>&nbsp; <a href="http://www.gotw.ca/publications/using_auto_ptr_effectively.htm">原文链接</a></h4> <p>大部分人都听说过auto_ptr指针，但是并非所有人都每天使用它。不使用它是不明智的（可耻的），因为auto_ptr的设计初衷是为了解决C++设计和编码的普遍问题，将它用好可以写出更健壮的代码。本文指出如何正确使用auto_ptr以使程序变得安全，以及如何避开危险，而不是一般使用auto_ptr的恶习所致的创建间歇性和难以诊断的问题。 </p> <h3>为什么它是一个“自动”指针 </h3> <p>auto_ptr只是许许多多智能指针中的一种。许多商业库提供许多更强大的智能指针，可以完成更多的事情。从可以管理引用计数到提供更先进的代理服务等。应该把auto_ptr认为是智能指针中的福特Escort[<a href="http://en.wikipedia.org/wiki/Ford_Escort_%28North_America%29" alt="go to wiki">注释</a>]：一个基于简单且通用目的的智能指针，既没有小发明也没有丰富的特殊目的更不需要高性能，但是能将许多普通的事情做好，并且能够适合日常使用的智能指针。 </p> <p>auto_ptr做这样一件事：拥有一个动态分配内存对象，并且在它不再需要的时候履行自动清理的职责。这里有个没有使用auto_ptr指针的不安全的例子： </p> <p><pre class="gc-code">    <span style="color: #008000">// Example 1(a): Original code</span>
    <span style="color: #008000">//</span>
    <span style="color: #0000ff">void</span> f()
    {
      T* pt( <span style="color: #0000ff">new</span> T );

      <span style="color: #008000">/*...more code...*/</span>

      <span style="color: #0000ff">delete</span> pt;
    }</pre>
<p></p>
<p>我们每天都像这样写代码，如果f()只是一个三行程序，也没做什么多余的事情，这样做当然可以很好工作。但是如果f()没有执行delete语句，比如程序提前返回（return）了，或者在执行的时候抛出异常了，然后就导致已经分配的对象没有被删除，因此我们就有了一个经典的内存泄漏。 </p>
<p>一个使Example（1）安全的办法是用一个“智能”的指针拥有这个指针，当销毁的时候，删除那个被指的自动分配的对象。因为这个智能指针被简单地用为自动对象（这就是，当它离开它的作用域的时候自动销毁对象），所以它被称作“自动”指针。 </p>
<p><pre class="gc-code">    <span style="color: #008000">// Example 1(b): Safe code, with auto_ptr</span>
    <span style="color: #008000">//</span>
    <span style="color: #0000ff">void</span> f()
    {
      auto_ptr&lt;T&gt; pt( <span style="color: #0000ff">new</span> T );

      <span style="color: #008000">/*...more code...*/</span>

    } <span style="color: #008000">// cool: pt's destructor is called as it goes out</span>
      <span style="color: #008000">// of scope, and the object is deleted automatically</span></pre>
<p></p>
<p>现在这段代码将不会再T对象上发生泄漏了，不必在意这个方法是正常退出还是异常退出，因为pt的析构函数将总是在堆栈弹出的时候被调用。清理工作将自动进行。</p>
<p>最后，使用auto_ptr和使用内建指针一样地容易，如果要“收回”资源并且再次手动管理的话，我们可以调用release()：</p><pre class="gc-code">    <span style="color: #008000">// Example 2: Using an auto_ptr</span>
    <span style="color: #008000">//</span>
    <span style="color: #0000ff">void</span> g()
    {
      T* pt1 = <span style="color: #0000ff">new</span> T;
      <span style="color: #008000">// right now, we own the allocated object</span>

      <span style="color: #008000">// pass ownership to an auto_ptr</span>
      auto_ptr&lt;T&gt; pt2( pt1 );

      <span style="color: #008000">// use the auto_ptr the same way</span>
      <span style="color: #008000">// we'd use a simple pointer</span>
      *pt2 = 12;       <span style="color: #008000">// same as "*pt1 = 12;"</span>
      pt2-&gt;SomeFunc(); <span style="color: #008000">// same as "pt1-&gt;SomeFunc();"</span>

      <span style="color: #008000">// use get() to see the pointer value</span>
      assert( pt1 == pt2.get() );

      <span style="color: #008000">// use release() to take back ownership</span>
      T* pt3 = pt2.release();

      <span style="color: #008000">// delete the object ourselves, since now</span>
      <span style="color: #008000">// no auto_ptr owns it any more</span>
      <span style="color: #0000ff">delete</span> pt3;

    } <span style="color: #008000">// pt2 doesn't own any pointer, and so won't</span>
      <span style="color: #008000">// try to delete it... OK, no double delete</span></pre>
<p>最后，我们可以使用auto_ptr的reset()方法将auto_ptr重置向另一个对象。如果auto_ptr已经获得一个对象，这个过程就像是它先删除已经拥有的对象，因此调用reset()，就像是先销毁了auto_ptr，然后重建了一个新的并拥有该新对象： </p>
<p><pre class="gc-code">    <span style="color: #008000">// Example 3: Using reset()</span>
    <span style="color: #008000">//</span>
    <span style="color: #0000ff">void</span> h()
    {
      auto_ptr&lt;T&gt; pt( <span style="color: #0000ff">new</span> T(1) );

      pt.reset( <span style="color: #0000ff">new</span> T(2) );
        <span style="color: #008000">// deletes the first T that was</span>
        <span style="color: #008000">// allocated with "new T(1)"</span>

    } <span style="color: #008000">// finally, pt goes out of scope and</span>
      <span style="color: #008000">// the second T is also deleted</span></pre>
<p></p>
<h3>包装指针数据成员</h3>
<p>同样，auto_ptr也可以被用于安全地包装指针数据成员。考虑下面使用Pimpl idiom（或者，编译器防火墙）的例子：<sup>[<a href="#1">1</a>]</sup></p>
<p><pre class="gc-code">    <span style="color: #008000">// Example 4(a): A typical Pimpl</span>
    <span style="color: #008000">//</span>

    <span style="color: #008000">// file c.h</span>
    <span style="color: #008000">//</span>
    <span style="color: #0000ff">class</span> C
    {
    <span style="color: #0000ff">public</span>:
      C();
      ~C();
      <span style="color: #008000">/*...*/</span>
    <span style="color: #0000ff">private</span>:
      <span style="color: #0000ff">class</span> CImpl; <span style="color: #008000">// forward declaration</span>
      CImpl* pimpl_;
    };

    <span style="color: #008000">// file c.cpp</span>
    <span style="color: #008000">//</span>
    <span style="color: #0000ff">class</span> C::CImpl { <span style="color: #008000">/*...*/</span> };

    C::C() : pimpl_( <span style="color: #0000ff">new</span> CImpl ) { }
    C::~C() { <span style="color: #0000ff">delete</span> pimpl_; }</pre>
<p></p>
<p>简单地说，就是C的私有细节被实现为一个单独的对象，藏匿于一个指针之中。该思路要求C的构造函数负责为隐藏在类内部的辅助“Pimpl”对象分配内存，并且C的析构函数负责销毁它。使用auto_ptr，我们会发现这非常容易： </p>
<p><pre class="gc-code">    <span style="color: #008000">// Example 4(b): A safer Pimpl, using auto_ptr</span>
    <span style="color: #008000">//</span>

    <span style="color: #008000">// file c.h</span>
    <span style="color: #008000">//</span>
    <span style="color: #0000ff">class</span> C
    {
    <span style="color: #0000ff">public</span>:
      C();
      <span style="color: #008000">/*...*/</span>
    <span style="color: #0000ff">private</span>:
      <span style="color: #0000ff">class</span> CImpl; <span style="color: #008000">// forward declaration</span>
      auto_ptr&lt;CImpl&gt; pimpl_;
    };

    <span style="color: #008000">// file c.cpp</span>
    <span style="color: #008000">//</span>
    <span style="color: #0000ff">class</span> C::CImpl { <span style="color: #008000">/*...*/</span> };

    C::C() : pimpl_( <span style="color: #0000ff">new</span> CImpl ) { }</pre>
<p></p>
<p>现在，析构函数不需要担心删除pimpl_指针了，因为auto_ptr将自动处理它。事实上，如果没有其它需要显式写析构函数的原因，我们完全不需要自定义析构函数。显然，这比手动管理指针要容易得多，并且将对象所有权包含进对象是一个不错的习惯，这正是auto_ptr所擅长的。我们将在最后再次回顾这个例子。 </p>
<h3>所有权，源，以及调用者(Sinks)</h3>
<p>它本身很漂亮，并且做得非常好：从函数传入或传出<span style="font-family: 'Courier New'" class="Code">auto_ptr</span>s，是非常有用的，比如函数的参数或者返回值。</p>
<p>让我们看看为什么，首先我们考虑当拷贝auto_ptr的时候会发生什么：一个auto_ptr获得一个拥有指针的对象，并且在同一时间只允许有一个auto_ptr可以拥有这个对象。当你拷贝一个auto_ptr的时候，你自动将源auto_ptr的所有权，传递给目标auto_ptr；如果目标auto_ptr已经拥有了一个对象，这个对象将先被释放。在拷贝完之后，只有目标auto_ptr拥有指针，并且负责在合适的时间销毁它，而源将被设置为空（null），并且不能再被当作原有指针的代表来使用。 </p>
<p>例如：</p>
<p><pre class="gc-code">    <span style="color: #008000">// Example 5: Transferring ownership from</span>
    <span style="color: #008000">//            one auto_ptr to another</span>
    <span style="color: #008000">//</span>
    <span style="color: #0000ff">void</span> f()
    {
      auto_ptr&lt;T&gt; pt1( <span style="color: #0000ff">new</span> T );
      auto_ptr&lt;T&gt; pt2;

      pt1-&gt;DoSomething(); <span style="color: #008000">// OK</span>

      pt2 = pt1;  <span style="color: #008000">// now pt2 owns the pointer,</span>
                  <span style="color: #008000">// and pt1 does not</span>

      pt2-&gt;DoSomething(); <span style="color: #008000">// OK</span>

    } <span style="color: #008000">// as we go out of scope, pt2's destructor</span>
      <span style="color: #008000">// deletes the pointer, but pt1's does nothing</span></pre>
<p></p>
<p>但是要避免陷阱再次使用已经失去所有权的auto_ptr： </p>
<p><pre class="gc-code">    <span style="color: #008000">// Example 6: Never try to do work through</span>
    <span style="color: #008000">//            a non-owning auto_ptr</span>
    <span style="color: #008000">//</span>
    <span style="color: #0000ff">void</span> f()
    {
      auto_ptr&lt;T&gt; pt1( <span style="color: #0000ff">new</span> T );
      auto_ptr&lt;T&gt; pt2;

      pt2 = pt1;  <span style="color: #008000">// now pt2 owns the pointer, and</span>
                  <span style="color: #008000">// pt1 does not</span>

      pt1-&gt;DoSomething();
                  <span style="color: #008000">// error! following a null pointer</span>
    }</pre>
<p></p>
<p>谨记于心，我们现在看看auto_ptr如何在源和调用者之间工作。“源”这里是指一个函数，或者其它创建一个新资源的操作，并且通常将移交出资源的所有权。一个“调用者”函数反转这个关系，也就是获得已经存在对象的所有权（并且通常还负责释放它）。而不是有一个源和调用者，返回并且利用一个秃头指针（译者注：而不是使用一个局部变量来传递这个指针），虽然，通过一个秃头指针来获得一个资源通常很好： </p>
<p><pre class="gc-code">    <span style="color: #008000">// Example 7: Sources and sinks</span>
    <span style="color: #008000">//</span>

    <span style="color: #008000">// A creator function that builds a new</span>
    <span style="color: #008000">// resource and then hands off ownership.</span>
    <span style="color: #008000">//</span>
    auto_ptr&lt;T&gt; Source()
    {
      <span style="color: #0000ff">return</span> auto_ptr&lt;T&gt;( <span style="color: #0000ff">new</span> T );
    }

    <span style="color: #008000">// A disposal function that takes ownership</span>
    <span style="color: #008000">// of an existing resource and frees it.</span>
    <span style="color: #008000">//</span>
    <span style="color: #0000ff">void</span> Sink( auto_ptr&lt;T&gt; pt )
    {
    }

    <span style="color: #008000">// Sample code to exercise the above:</span>
    auto_ptr&lt;T&gt; pt( Source() ); <span style="color: #008000">// takes ownership</span></pre>
<p></p>
<p>注意下面的微妙的变化： </p>
<p>
<ol>
<li>
<p>Source()分配了一个新对象并且以一个完整安全的方式将它返回给调用者，并让调用者成为指针的拥有着。即使调用者忽略了返回值（显然，如果调用者忽略了返回值，你应该从来没有写过代码来删除这个对象，对吧？），分配的对象也将被自动安全地删除。</p>
<p>在本文的最后，我将演示返回一个auto_ptr是一个好习惯。让返回值包裹进一些东西比如auto_ptr通常是使得函数变得强健的有效方式。 </p>
<li>
<p>Sink()通过传值的方式获得对象所有权。当执行完Sink()的时候，当离开作用域的时候，删除操作将被执行（只要Sink()没有将所有权转移）。上面所写的Sink()函数实际上并没有对参数做任何事情，因此调用“Sink(pt);”就等于写了“pt.reset(0);”，但是大部分的Sink函数都将在释放它之前做一些工作。 </p></li></ol>
<p></p>
<h3>不可以做的事情，以及为什么不能做 </h3>
<p>谨记：千万不要以我之前没有提到的方式使用<span style="font-family: 'Courier New'" class="Code">auto_ptr</span>s。我已经看见过很多程序员试着用其他方式写<span style="font-family: 'Courier New'" class="Code">auto_ptr</span>s就像他们在使用其它对象一样。但问题是auto_ptr并不像其他对象。这里有些基本原则，我将把它们提出来以引起你的注意： </p>
<p><b>For </b><span class="Code"><b><span style="font-family: 'Courier New'">auto_ptr</span></b></span><b>, copies are NOT equivalent.<o:p> </o:p></b>（复制auto_ptr将与原来的不相等） </p>
<p>当你试着在一般的代码中使用<span style="font-family: 'Courier New'" class="Code">auto_ptr</span>s的时候，它将执行拷贝，并且没有任何提示，拷贝是不相等的（结果，它确实就是拷贝）。看下面这段代码，这是我在C++新闻组经常看见的： </p>
<p></p>
<p><pre class="gc-code">    <span style="color: #008000">// Example 8: Danger, Will Robinson!</span>
    <span style="color: #008000">//</span>
    vector&lt; auto_ptr&lt;T&gt; &gt; v;

    <span style="color: #008000">/* ... */</span>

    sort( v.begin(), v.end() );</pre>
<p></p>
<p></p>
<p>在标准容器中使用<span style="font-family: 'Courier New'" class="Code">auto_ptr</span>s<i>总是</i>不安全的。一些人可能要告诉你，他们的编译器或者类库能够很好地编译它们，而另一些人则告诉你在某一个流行的编译器的文档中看到这个例子，不要听他们的。 </p>
<p>问题是<span style="font-family: 'Courier New'" class="Code">auto_ptr</span>并不完全符合一个可以放进容器类型的前提，因为拷贝<span style="font-family: 'Courier New'" class="Code">auto_ptr</span>s是不等价的。首先，没有任何东西说明，vector不能决定增加并制造出“扩展”的内部拷贝。再次，当你调用一个一般函数的时候，它可能会拷贝元素，就像sort()那样，函数必须有能力假设拷贝是等价的。至少一个流行的排序拷贝“核心”的元素，如果你试着让它与<span style="font-family: 'Courier New'" class="Code">auto_ptr</span>s一起工作的话，它将拷贝一份“核心”的auto_ptr对象（因此转移所有权并且将所有权转移给一个临时对象），然后对其余的元素也采取相同的方式（从现有成员创建更多的拥有所有权的auto_ptr），当排序完成后，核心元素将被销毁，并且你将遇到一个问题：这组序列里至少一个auto_ptr（也就是刚才被掉包的那个核心元素）不再拥有对象所有权，而那个真实的指针已经随着临时对象的销毁而被删除了！ </p>
<p>于是标准委员会回退并希望做一些能够帮助你避免这些行为的事情：标准的auto_ptr被故意设计成当你希望在使用标准容器的时候使用它时打断你（或者，至少，在大部分的标准库实现中打断你）。为了达到这个目的，标准委员会利用这样一个技巧：让<span style="font-family: 'Courier New'" class="Code">auto_ptr</span>'s的拷贝构造函数和赋值操作符的右值（rhs）指向非常量。因为标准容器的单元素insert()函数，需要一个常量作为参数，因此<span style="font-family: 'Courier New'" class="Code">auto_ptr</span>s在这里就不工作了。（译者注：右值不能赋值给非常量） </p>
<h3>使用const auto_ptr是一个好习惯</h3>
<p>将一个auto_ptr设计成<span style="font-family: 'Courier New'" class="Code">const auto_ptr</span>s将不再丢失所有权：拷贝一个const auto_ptr是违法的（译者注：没有这样的构造函数），实际上你可以针对它做的唯一事情就是通过operator*()或者operator-&gt;()解引用它或者调用get()来获得所包含的指针的值。这意味着我们有一个简单明了的风格来表达一个绝不丢失所有权的auto_ptr： </p>
<p><pre class="gc-code">    <span style="color: #008000">// Example 9: The const auto_ptr idiom</span>
    <span style="color: #008000">//</span>
    <span style="color: #0000ff">const</span> auto_ptr&lt;T&gt; pt1( <span style="color: #0000ff">new</span> T );
        <span style="color: #008000">// making pt1 const guarantees that pt1 can</span>
        <span style="color: #008000">// never be copied to another auto_ptr, and</span>
        <span style="color: #008000">// so is guaranteed to never lose ownership</span>

    auto_ptr&lt;T&gt; pt2( pt1 ); <span style="color: #008000">// illegal</span>
    auto_ptr&lt;T&gt; pt3;
    pt3 = pt1;              <span style="color: #008000">// illegal</span>
    pt1.release();          <span style="color: #008000">// illegal</span>
    pt1.reset( <span style="color: #0000ff">new</span> T );     <span style="color: #008000">// illegal</span></pre>
<p></p>
<p>这就是我要说的cosnt！因此如果现在你要向世界证明你的auto_ptr是不会被改变并且将总是删除其所有权，加上const就是你要做的。const auto_ptr风格是有用的，你必须将它谨记于心。 </p>
<h3>auto_ptr以及异常安全</h3>
<p>最后，auto_ptr对写出异常安全的代码有时候非常必要，思考下面的代码：</p>
<p><pre class="gc-code">    <span style="color: #008000">// Example 10(a): Exception-safe?</span>
    <span style="color: #008000">//</span>
    String f()
    {
      String result;
      result = "<span style="color: #8b0000">some value</span>";
      cout &lt;&lt; "<span style="color: #8b0000">some output</span>";
      <span style="color: #0000ff">return</span> result;
    }</pre>
<p></p>
<p>该函数有两个可见的作用：它输出一些内容，并且返回一个String。关于异常安全的详细说明超出了本文的范围<sup><a href="#2">[2]</a></sup>，但是我们想要取得的目标就是强异常安全的保障，归结为确保函数的原子性——如果有异常，所有的作用一起发生或者都不发生。 </p>
<p>虽然在例10(a)中的代码非常精巧，看起来相当接近于异常安全的代码，但仍然有一些小的瑕疵，就像下面的客户代码所示： </p>
<p><pre class="gc-code">    String theName;
    theName = f();</pre>
<p></p>
<p>因为结果通过值返回，因此String的拷贝构造函数将被调用，而拷贝赋值操作符被调用来将结果拷贝到theName中。如果任何一个拷贝失败了，f()就完成了所有它的工作以及所有它的任务（这很好），但是结果是无法挽回的（哎哟我的妈呀） </p>
<p>我们可以做的更好吗，是否可以通过避免拷贝来避免这个问题？例如，我们可以 让函数有一个非常量引用参数并向下面这样返回值： </p>
<p><pre class="gc-code">    <span style="color: #008000">// Example 10(b): Better?</span>
    <span style="color: #008000">//</span>
    <span style="color: #0000ff">void</span> f( String&amp; result )
    {
      cout &lt;&lt; "<span style="color: #8b0000">some output</span>";
      result = "<span style="color: #8b0000">some value</span>";
    }</pre>
<p></p>
<p>这看起来很棒，但实际不是这样的，返回result的赋值的函数只完成了一个功能，而将其它事情留给了我们。它仍然会出错。因此这个做法不可取。 </p>
<p>解决这个问题的一个方法是返回一个指向动态分配指针的String对象，但是最好的解决方案是让我们做的更多，返回一个指针包含在auto_ptr： </p>
<p><pre class="gc-code">    <span style="color: #008000">// Example 10(c): Correct (finally!)</span>
    <span style="color: #008000">//</span>
    auto_ptr&lt;String&gt; f()
    {
      auto_ptr&lt;String&gt; result = <span style="color: #0000ff">new</span> String;
      *result = "<span style="color: #8b0000">some value</span>";
      cout &lt;&lt; "<span style="color: #8b0000">some output</span>";
      <span style="color: #0000ff">return</span> result;  <span style="color: #008000">// rely on transfer of ownership;</span>
                      <span style="color: #008000">// this can't throw</span>
    }</pre>
<p></p>
<p>这里是一个技巧，当我们有效隐藏所有的工作来构造第二个功能（返回值）当确保它可以被安全返回给调用者并且在第一个功能（打印消息）完成的时候没有抛出操作。我们知道一旦cout完成，返回值将成功交到调用者手中，并且无论如何都会正确清理：如果调用者接受返回值，调用者将得到这个拷贝的auto_ptr临时对象的所有权；如果调用者没有接受返回值，也就是忽略返回值，分配的String将在临时auto_ptr被销毁的时候自动清理。这种安全扩展的代价呢？就像我们经常实现的强异常安全一样，强安全通常消耗一些效率（通常比较小）——这里指额外的动态内存分配。但是当我们在效率和正确性之间做出选择的话，我们通常会选择后者！ </p>
<p>让我们养成在日常工作中使用auto_ptr的习惯。auto_ptr解决了常见的问题，并且能够使你的代码变得更安全和健壮，特别是它可以防止内存泄漏以及确保强安全。因为它是标准的，因此它在不同类库和平台之间是可移植的，因此无论你在哪里使用它，它都将是对的。 </p>
<h3>致谢</h3>
<p>This article is drawn from material in the new book Exceptional C++: 47 engineering puzzles, programming problems, and exception-safety solutions by Herb Sutter, © 2000 Addison Wesley Longman Inc., which contains further detailed treatments of points touched on briefly in this article, including exception safety, the Pimpl (compiler-firewall) Idiom, optimization, const-correctness, namespaces, and other C++ design and programming topics.</p>
<h3>注释</h3>
<ol>
<li>
<p><a name="1"></a>Pimpl风格可以有效减少项目构建时间，因为它在C私有部分改变的时候，阻止客户代码引起广泛的重新编译。更多关于Pimpl风格以及如何部署编译器墙，参考这本<a href="xc++.htm">Exceptional C++</a></i>的条款26到30。（Addison-Wesley, 2000） </p>
<li>
<p><a name="2"></a>See the article <span><a href="http://www.informit.com/content/downloads/aw/meyerscddemo/DEMO/MAGAZINE/SU_FRAME.HTM">"Exception-Safe Generic Containers"</a></span> originally published in <em>C++ Report</em> and available on the <i>Effective C++ CD</i> (Scott Meyers, Addison-Wesley, 1999) and Items 8 to 19 in <i><a href="xc++.htm">Exceptional C++</a></i> (Herb Sutter, Addison-Wesley, 2000).</p></li></ol><img src ="http://www.cppblog.com/mymsdn/aggbug/111882.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mymsdn/" target="_blank">volnet</a> 2010-04-07 19:08 <a href="http://www.cppblog.com/mymsdn/archive/2010/04/07/Using-auto_ptr-Effectively.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]从C++的Return Value Optimization (RVO)到C#的value type</title><link>http://www.cppblog.com/mymsdn/archive/2010/04/06/111777.html</link><dc:creator>volnet</dc:creator><author>volnet</author><pubDate>Tue, 06 Apr 2010 11:42:00 GMT</pubDate><guid>http://www.cppblog.com/mymsdn/archive/2010/04/06/111777.html</guid><wfw:comment>http://www.cppblog.com/mymsdn/comments/111777.html</wfw:comment><comments>http://www.cppblog.com/mymsdn/archive/2010/04/06/111777.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mymsdn/comments/commentRss/111777.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mymsdn/services/trackbacks/111777.html</trackback:ping><description><![CDATA[<p>先看一段简单的C++代码： <pre class="gc-code">Type get(<span style="color: #0000ff">int</span> I){
    <span style="color: #0000ff">return</span> Type(i);
} 

Type t = get(1); </pre>
<p>这里， 我们从C++的基本语义看上去， 应该是Type(i) 调用一次拷贝构造函数， 在堆栈中生成一个临时对象；然后，用该对象构造返回对象；然后对这个临时对象调用析构函数；在调用者方， 用返回的临时对象调用拷贝构造函数以初始化对象t, 返回对象的析构函数在这之后， 函数返回之前调用。 
<p>所以， Type t = get(i); 应该有三个拷贝构造函数和两个析构函数的调用. 
<p>可是， 还有一种说法是， 编译器可能会对这两个临时对象进行优化，最终的优化结果会是只有一次的构造函数。因为很明显地可以看到， 这里我们其实只是要用一个整数构造一个Type对象。 
<p>嗯. 似乎很有道理！ 
<p>那么， 哪一种说法对呢？ 没有调查就没有发言权，于是本人用VC++6.0做了实验。 放了些cout&lt;&lt;…..在拷贝构造函数里，观察打印的结果， 结果却是跟我的simple, naïve的预测一致。三个拷贝构造函数， 两个析构函数。 
<p>“你个弱智编译器！脑袋进水了吧？”(忘了编译器没脑袋了)“很明显在这个例子里我的两个临时对象都没有用的啊！” 
<p>于是，上网， 查资料， google一下吧！ 
<p>下面是我查到的一些结果： 
<p>其实， 这种对值传递的优化的研究， 并不只局限于返回值。对下面这个例子： <pre class="gc-code"><span style="color: #0000ff">void</span> f(T t) { } 
<span style="color: #0000ff">void</span> main(<span style="color: #0000ff">void</span>){ 
    T t1;
    f(t1); 
} </pre>
<p>也有这种考虑。 
<p>f(T)是按值传递的。语义上应该做一个复制， 使得函数内部对T的改变不会影响到原来的t1. 
<p>但是，因为在调用f(t1)之后， 我们没有再使用t1(除了一个隐含的destructor调用)，是否可能把复制优化掉， 直接使用t1呢？这样可以节省掉一个拷贝构造函数和一个析构函数。 
<p>可是， 不论是对返回值的优化， 还是对上面这种局部对象的优化，在1995年的C++新标准草案出台前都是为标准所严格限制的 (虽然有些编译器并没有遵行这个标准， 还是支持了这种“优化”) 
<p>那么， 这又是为什么呢？ 
<p>这里面涉及到一个普遍的对side-effect的担忧。 
<p>什么又是side-effect呢？ 
<p>所谓side-effect就是一个函数的调用与否能够对系统的状态造成区别。 
<p>int add(int i, int j){ return i+j; }就是没有side-effect的，而 
<p>void set(int* p, int I, int v){ p[I]=v; }就是有side-effect的。因为它改变了一个数组元素的值， 而这个数组元素在函数外是可见的。 
<p>通常意义上来说， 所有的优化应该在不影响程序的可观察行为的基础上进行的。否则，快则快了， 结果却和所想要的完全不同！ 
<p>而C++的拷贝构造函数和析构函数又很多都是有side-effect的。如果我们的“优化”去掉了一个有side-effect的拷贝构造函数和一个析构函数， 这个“优化”就有可能改变程序的可观察行为。（注意， 我这里说的是“可能”，因为“负负得正”， 两个有side-effect的函数的调用， 在不考虑并行运行的情况下， 也许反而不会影响程序的可观察行为。不过， 这种塞翁失马的事儿， 编译器就很难判断了） 
<p>基于这种忧虑, 1995年以前的标准， 明确禁止对含有side-effect的拷贝构造函数和析构函数的优化。同时， 还有一些对C++扩充的提议， 考虑让程序员自己对类进行允许优化的声明。 程序员可以明确地告诉编译器：不错， 我这个拷贝构造函数， 析构函数是有side-effect, 但你别管， 尽管优化， 出了事有我呢！ 
<p>哎， side-effect真是一个让人又恨又爱的东西！它使编译器的优化变得困难；加大了程序维护和调试的难度。因此 functional language 把side-effect当作洪水猛兽一样，干脆禁止。但同时，我们又很难离开side-effect. 不说程序员们更习惯于imperative 的编程方法, 象数据库操作，IO操作都天然就是side-effect. 
<p>不过，个人还是认为C++标准对“优化”的保守态度是有道理的。无论如何，让“优化”可以潜在地偷偷地改变程序的行为总是让人想起来就不舒服的。 
<p>但是， 矛盾是对立统一的。（想当年俺马列可得了八十多分呢）。 对这种aggressive的“优化”的呼声是一浪高过一浪。 以Stan Lippeman为首的一小撮顽固分子对标准的颠覆和和平演变的阴谋从来就没有停止过。 这不？在1996年的一个风雨交加的夜晚， 一个阴险的C++新标准草案出炉了。在这个草案里， 加入了一个名为RVO (Return Value Optimization) 的放宽对优化的限制， 妄图走资本主义道路， 给资本家张目的提案。其具体内容就是说：允许编译器对命名过的局部对象的返回进行优化， 即使拷贝构造函数/析构函数有side-effect也在所不惜。这个提议背后所隐藏的思想就是：为了提高效率， 宁可冒改变程序行为的风险。宁要资本主义的苗， 不要社会主义的草了！ 
<p>我想， 这样的一个罪大恶极的提案竟会被提交，应该是因为C++的值拷贝的语义的效率实在太“妈妈的”了。 当你写一个 Complex operator+(const Complex&amp; c1, const Complex&amp; c2);的时候， 竟需要调用好几次拷贝构造函数和析构函数！同志们！（沉痛地， 语重心长地）社会主义的生产关系的优越性怎么体现啊？ 
<p>接下来， 当我想Google C++最新的标准， 看RVO是否被最终采纳时， 却什么也找不到了。 到ANSI的网站上去， 居然要付钱才能DOWNLOAD文档。 “老子在城里下馆子都不付钱， down你几个烂文档还要给钱？！” 
<p>故事没有结局， 实在是不爽。 也不知是不是因为标准还没有敲定， 所以VC++6 就没有优化， 还是VC根本就没完全遵守标准。 
<p>不过，有一点是肯定的。 当写程序的时候， 最好不要依赖于RVO (有人， 象Stan Lippeman, 又叫它NRV优化)。 因为， 不论对标准的争论是否已经有了结果， 实际上各个编译器的实现仍还是各自为政， 没有统一。 一个叫SCOtt Meyers的家伙(忘了是卖什么的了)就说， 如果你的程序依赖于RVO, 最好去掉这种依赖。也就是说， 不管RVO到底标准不标准， 你还是不能用。 不仅不能用， 还得时刻警惕着RVO可能带来的程序行为上的变化。 （也不知这帮家伙瞎忙了半天到底为啥！） 
<p>说到这里， 倒想起了C#里一个困惑了我很久的问题。记得读C#的specification的时候， 非常不解为什么C#不允许给value type 定义析构函数。 
<p>这里， 先简略介绍一下C#里的value type (原始数据类型， struct 类型)。 
<p>在C#里的value_type就象是值， 永远只能copy, 取值。因此， 它永远是in-place的。如果你把一个value type的数据放在一个对象里，它的生命期就和那个对象相同；如果你声明一个value type 的变量在函数中， 它的生命期就在lexical scope里。 
<p>{ 
<p>&nbsp; The_ValueType value; 
<p>}//value 到这里就死菜了 
<p>啊呀呀！ 这不正是我们怀念的C++的stack object吗？ 
<p>在C++里，Auto_ptr, shared_ptr, 容器们， 不都是利用析构函数来管理资源的吗？ 
<p>C#，Java 虽然利用garbage collection技术来收集无用对象， 使我们不用再担心内存的回收。 但garbage collection并不保证无用对象一定被收集， 并不保证Dispose()函数一定被调用， 更不保证一个对象什么时候被回收。 所以对一些非内存的资源， 象数据库连接， 网络连接， 我们还是希望能有一个类似于smart pointer的东西来帮我们管理啊。（try-finally 虽然可以用， 但因为它影响到lexical scope, 有时用起来不那么方便） 
<p>于是， 我对C#的取消value type的析构函数充满了深厚的阶级仇恨。 
<p>不过， 现在想来， C#的这种设计一定是惩于C++失败的教训： 
<p>&nbsp;&nbsp; 1. value type 没有拷贝构造函数。C#只做缺省copy, 没有side-effect<br>&nbsp;&nbsp; 2. value type 不准有析构函数。C#有garbage collection, 析构函数的唯一用途只会是做一些side-effect象关闭数据库连接。 所以取消了析构函数， 就取消了value type的side-effect.<br>&nbsp;&nbsp; 3. 没有了side-effect, 系统可以任意地做优化了 
<p>对以下程序： 
<p>The_Valuetype get(int I){return The_Valuetype(i);} 
<p>The_Valuetype t = get(1); 
<p>在C#里我们可以快乐地说：只调用了一次构造函数。 再没有side-effect的沙漠， 再没有难以优化的荒原， smart pointer望而却步， 效率之花处处开遍。 I have a dream, ……</p>
<p>转载自：<a title="http://gugu99.itpub.net/post/34143/466008" href="http://gugu99.itpub.net/post/34143/466008">http://gugu99.itpub.net/post/34143/466008</a></p><img src ="http://www.cppblog.com/mymsdn/aggbug/111777.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mymsdn/" target="_blank">volnet</a> 2010-04-06 19:42 <a href="http://www.cppblog.com/mymsdn/archive/2010/04/06/111777.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于线程同步的一些总结（用户模式/内核模式）</title><link>http://www.cppblog.com/mymsdn/archive/2009/12/26/about-multithread-sync.html</link><dc:creator>volnet</dc:creator><author>volnet</author><pubDate>Fri, 25 Dec 2009 21:53:00 GMT</pubDate><guid>http://www.cppblog.com/mymsdn/archive/2009/12/26/about-multithread-sync.html</guid><wfw:comment>http://www.cppblog.com/mymsdn/comments/104111.html</wfw:comment><comments>http://www.cppblog.com/mymsdn/archive/2009/12/26/about-multithread-sync.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mymsdn/comments/commentRss/104111.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mymsdn/services/trackbacks/104111.html</trackback:ping><description><![CDATA[<p><h3>自旋锁同步</h3><ol><li>一般是为了内核态下各个派遣函数之间做同步作用的。</li><li>原理是（单CPU）将IRQL从软件中断提升到硬件中断。PASSIVE_LEVEL->DISPATCH_LEVEL。因为在DISPATCH_LEVEL中是不会出现线程切换的（只有高级别能打断低级别，而低级别不能打断高级别）。</li><li>因为分页内存将导致如果线程切换的时候会引起分页数据交换，数据交换是通过引发页故障来实现的，而页故障是不允许出现在DISPATCH_LEVEL中的，否则将引起系统崩溃（PASSIVE_LEVEL则允许）。驱动程序的StartIO例程、DPC例程、中断服务例程都运行在DISPATCH_LEVEL或者更高的IRQL。因此这些例程不能使用分页内存，否则将导致系统崩溃。</li><li>自旋锁在不同IRP之间同步的时候，则需要放在DeviceExtension中传递。</li></ol></p><p><h3>互锁</h3><ol><li>类似于number++; //汇编后将不止一条语句，非原子操作number--; //同上因为语句会变成多句，在线程切换的时候，两个线程下的该例程将会交织在一起执行，导致错误。可以：<div class="gc-code">先加锁<br />number++;<br />解锁<br />再加锁<br />number--;<br />解锁<br /></div>来实现两句话的同步（按指定顺序执行，而不受到线程切换的影响）加锁解锁可以使用自旋锁</li><li>在系统中提供了Interlocked***/ExInterlocked***实现</li></ol></p><p><h3>信号灯同步</h3><ol><li>线程1关闭信号灯，以至于使用Wait****的时候，当前线程处于暂停状态。</li><li>线程2的作用就是在执行结束后，点亮信号灯（增加计数器）。当线程切换回来的时候，线程1就因为计数器不是0而使信号灯处于激活状态，从而继续执行线程1。</li></ol></p><p><h3>事件的同步</h3>（不能递归获取互斥体）<ol><li>主线程在辅助线程上设置了事件，如果不使用Wait**等待事件返回，则主线程可能直接执行完毕了，而导致辅助线程还在执行。</li><li>使用Wait****可以使主线程等待事件执行完成。</li></ol></p><p><h3>互斥体同步</h3>（允许递归获取互斥体（得到互斥体的线程还可以再次获得这个互斥体，或者说互斥体对于已经获得互斥体的线程不产生&#8220;互斥&#8221;关系））<ol><li>创建一个互斥体对象，将互斥体对象传递给多个线程。</li><li>在每个线程操作的步骤中，调用Wait*****，如果互斥体处于激活（内部维护一个计数器），则继续执行后续代码，并在调用结束后恢复互斥体Release****，这样当别的线程试图使用互斥体后面的代码的时候，因为互斥体状态未激活，则无法继续执行代码。</li></ol></p><p><h3>快速互斥体同步</h3><ol><li>与互斥体同步类似，唯一区别是不允许递归获取互斥体</li></ol></p><img src ="http://www.cppblog.com/mymsdn/aggbug/104111.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mymsdn/" target="_blank">volnet</a> 2009-12-26 05:53 <a href="http://www.cppblog.com/mymsdn/archive/2009/12/26/about-multithread-sync.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何编译TrueCrypt源码</title><link>http://www.cppblog.com/mymsdn/archive/2009/12/23/how-to-complie-TrueCrypt-63a.html</link><dc:creator>volnet</dc:creator><author>volnet</author><pubDate>Wed, 23 Dec 2009 15:47:00 GMT</pubDate><guid>http://www.cppblog.com/mymsdn/archive/2009/12/23/how-to-complie-TrueCrypt-63a.html</guid><wfw:comment>http://www.cppblog.com/mymsdn/comments/103863.html</wfw:comment><comments>http://www.cppblog.com/mymsdn/archive/2009/12/23/how-to-complie-TrueCrypt-63a.html#Feedback</comments><slash:comments>7</slash:comments><wfw:commentRss>http://www.cppblog.com/mymsdn/comments/commentRss/103863.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mymsdn/services/trackbacks/103863.html</trackback:ping><description><![CDATA[<h3>相关配置</h3><ul>&#160;&#160;&#160; <li>Intel x86 Core 2 Duo</li>&#160;&#160;&#160; <li>Windows 7 Ultimate x86 version</li>&#160;&#160;&#160; <li>Windows Driver Develop Kit 7600.16385.0</li>&#160;&#160;&#160; <li>TrueCrypt 6.3a Source.zip</li>&#160;&#160;&#160; <li>Microsoft Visual Studio 2008 SP1 (VC++ 2008)</li>&#160;&#160;&#160; <li>Microsoft Visual Studio VC++ 1.52</li>&#160;&#160;&#160; <li>NASM version 2.07 compiled on Jul 19 2009</li>&#160;&#160;&#160; <li>gzip 1.2.4 Win32 (02 Dec 97)</li>&#160;&#160;&#160; <li>......</li></ul><br /><h3>配置TrueCrypt</h3><ol><li>下载MSVC++ 1.52，安装在C盘下：C:\MSVC<br /></li><li>下载NASM，也安装在C盘下：C:\NASM<br />http://www.nasm.us/pub/nasm/releasebuilds/2.07/win32/<br /></li><li>下载GZIP，也安装在C盘下：C:\gzip<br /></li><li>下载并安装WINDDK，http://www.microsoft.com/whdc/DevTools/WDK/WDKpkg.mspx<br />我将它们安装在D盘，路径：D:\WinDDK<br /></li><li>设置系统变量（（WIN7）控制面板\所有控制面板项\系统\高级系统设置\环境变量）：系统变量中，新增：<br />变量1：MSVC16_ROOT&#160;&#160;&#160; 值：C:\MSVC<br />变量2：WINDDK_ROOT&#160;&#160;&#160; 值：D:\WinDDK\7600.16385.0<br />其中7600.16385.0为WinDDK的第二级目录名，同时也是版本号，7600是Windows7的发行版本号。<br />双击变量：PATH，在其值的末尾补上：C:\NASM;C:\gzip<br />目的是为了让我们可以直接在命令行实用nasm以及gzip作为命令行。<br /></li><li>下载PKCS11，三个文件，右键另存为即可。<br />http://svn.openvpn.net/projects/openvpn/test/time/openvpn/pkcs11-headers/<br />将三个文件（pkcs11.h、pkcs11f.h、pkcs11t.h）拷贝到源码下的Common文件夹下，我的源码放在D盘根目录，三个源码我就放在&#8220;D:\TrueCrypt\Common&#8221;文件夹中。<br /></li><li>编译，会发现有两个错误。<br />CKR_NEW_PIN_MODE和CKR_NEXT_OTP未定义，补充定义一下即可。<br />在d:\TrueCrypt\Common\pkcs11t.h文件里（请根据您自己的路径进行复制）<br />这里将它们设置为：<br /><div class="gc-code">#define CKR_NEW_PIN_MODE&#160;&#160;&#160;&#160;&#160; 0x000001B0<br />#define CKR_NEXT_OTP&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; 0x000001B1<br /></div>我的方法是找到实用它的语句附近的同类语句，找到相同的定义，在其下方添加。<br />比如：<br /><div class="gc-code">&#160;&#160;&#160; &#160;&#160;&#160; &#160;&#160;&#160; TC_TOKEN_ERR (CKR_MUTEX_NOT_LOCKED)<br />&#160;&#160;&#160; &#160;&#160;&#160; &#160;&#160;&#160; TC_TOKEN_ERR (CKR_NEW_PIN_MODE)<br />&#160;&#160;&#160; &#160;&#160;&#160; &#160;&#160;&#160; TC_TOKEN_ERR (CKR_NEXT_OTP)<br /></div>这三句话放在一起，后两句有问题，但第一句正常，则查找CKR_MUTEX_NOT_LOCKED的存放位置，在其下方添加如上两句，其中定义的值参考<br />http://www.cryptsoft.com/pkcs11doc/STANDARD/include/v220/otp-pkcs11.h，这里的值只不过是一种错误码，只要它不重复，就可以了。<br /></li><li>再编译，可能会遇到一些警告：<br /><ol><li>nasm.exe正在停止，而因为没有正确执行，又导致&#8220;fatal error LNK1136: invalid or corrupt file&#8221;错误。<br />遇到这个可能是因为你的nasm正在试图编译ase_amdX64.asm文件，而nasm对64位的asm编译对你可能意义不大，起码对我而言是这样的，于是，我就将它转成编译x86体系架构的，也许是因为参数配置的问题，你可以尝试别的方案，如果有更好的话，请告诉我。<br />这里我搜索：x64、asm等关键字，修改d:\TrueCrypt\Crypto\Makefile.inc文件为下面这样即可：<br /><div class="gc-code">行1&#160;&#160;&#160; !if "$(TC_ARCH)" == "x86"<br />行2&#160;&#160;&#160; TC_OBJ_FORMAT = win32<br />行3&#160;&#160;&#160; !else<br />行4&#160;&#160;&#160; #TC_OBJ_FORMAT = win64<br />行5&#160;&#160;&#160; #edit by gocool, if the x64 system need the nasm.exe use the x64 format parameters for executing.<br />行6&#160;&#160;&#160; #abort the x64 system here for building.<br />行7&#160;&#160;&#160; #2009/12/23<br />行8&#160;&#160;&#160; TC_OBJ_FORMAT = win32<br />行9&#160;&#160;&#160; TC_ARCH = x86<br />行10&#160;&#160;&#160; !endif<br />行11&#160;&#160;&#160; <br />行12&#160;&#160;&#160; "$(OBJ_PATH)\$(O)\Aes_$(TC_ARCH).obj": Aes_$(TC_ARCH).asm<br />行13&#160;&#160;&#160; &#160;&#160;&#160; nasm.exe -Xvc -f $(TC_OBJ_FORMAT) -Ox -D DLL_EXPORT -o "$@" -l "$(OBJ_PATH)\$(O)\Aes_$(TC_ARCH).lst" Aes_$(TC_ARCH).asm<br /></div>其中，为了减少变化量，也利于以后恢复，第4-7行为注释，第8、9行我将非x86的情况也定义成x86的情况，这样无论如何下面第13行的语句都将执行以x86体系为结构的设置，而这样的设置通常是正确的。<br /></li><li>fatal error LNK1000: Internal error during IncrBuildImage<br />据说是Microsoft Visual Studio 2008的一个BUG。http://blog.csdn.net/just_one_two/archive/2009/10/05/4634391.aspx<br />听说有两种方法，一种是方法1，需要下载补丁，我没有尝试。第二种通过修改配置的方法我成功了，步骤如下：<br />方法：项目->属性->链接器->常规&#160;&#160; 下面的&#8220;启用增量链接&#8221;，将&#8220;是(/INCREMENTAL)&#8221;改为&#8220;否(/INCREMENTAL:NO)&#8221;。<br />不过这又引入了另外一个警告：3>FormatCom.obj : warning LNK4075: 忽略&#8220;/EDITANDCONTINUE&#8221;(由于&#8220;/INCREMENTAL:NO&#8221;规范)<br />选择项目,属性->配置属性->C/C++，修改&#8220;调试信息格式&#8221;为&#8220;程序数据库(/Zi)&#8221;即可。<br /></li><li>警告：未找到下列环境变量<br /><div class="gc-code">4>项目 : warning PRJ0018 : 未找到下列环境变量:<br />4>$(PKCS11_INC)<br /></div>由于项目属性里设置有附加包含目录&#8220;$(PKCS11_INC)&#8221;，因此编译的时候会在系统变量里寻找PKCS11_INC项目，如果找不到，则给出警告，因此，我们需要手动补充这个项。方法同步骤5，增加一个变量为PKCS11_INC，值：D:\TrueCrypt\Common，其中，值就是之前我们拷贝三个文件（pkcs11.h、pkcs11f.h、pkcs11t.h）的目录。<br /></li><li>如果不出意外的话，你可能还会得到一个使用了PKEY_AppUserModel_ID未定义的声明符的错误。这个是用于标识用户态应用程序的唯一标识。你可以在Setup.h文件中定义：<div class="gc-code">/*---region add by gc---*/<br />#include "wtypes.h"<br />&#160;&#160;&#160; const PROPERTYKEY PKEY_AppUserModel_ID = {<br />&#160;&#160;&#160; &#160;&#160;&#160; {<br />&#160;&#160;&#160; &#160;&#160;&#160; &#160;&#160;&#160; (unsigned long)2009,/*unsigned long&#160; Data1;*/<br />&#160;&#160;&#160; &#160;&#160;&#160; &#160;&#160;&#160; (unsigned short)12,/*unsigned short Data2;*/<br />&#160;&#160;&#160; &#160;&#160;&#160; &#160;&#160;&#160; (unsigned short)23,/*unsigned short Data3;*/<br />&#160;&#160;&#160; &#160;&#160;&#160; &#160;&#160;&#160; 0x44,0x55,0x55,0x55,0x55,0x55,0x55,0x55<br />&#160;&#160;&#160; &#160;&#160;&#160; },/*GUID fmtid;*/<br />&#160;&#160;&#160; &#160;&#160;&#160; (DWORD)PID_FIRST_USABLE /*DWORD pid;*/<br />&#160;&#160;&#160; };<br />/*---endregion---*/<br /></div>其中，这个结构体是由GUID和PID共同组成的。<br /></li></ol></li></ol><h3>下载链接</h3><li>TrueCrypt下载：http://www.sfr-fresh.com/windows/misc/TrueCrypt-6.2a-Source.zip:a/Boot/Windows/Makefile</li><li>WinDDK下载：http://www.microsoft.com/whdc/DevTools/WDK/WDKpkg.mspx</li><li>PKCS11下载：http://svn.openvpn.net/projects/openvpn/test/time/openvpn/pkcs11-headers/</li><li>GZip下载：http://www.gzip.org/ 或者 http://www.gzip.org/gz124src.zip</li><li>Nasm下载：http://www.nasm.us/pub/nasm/releasebuilds/2.07/win32/</li><li>MSVC1.52下载：http://download.csdn.net/source/620960 （15.02MB）（似乎网上很多人都在找1.52（最后一个可以编译16bit程序的VC编译器），但官方网站上没有公开下载的链接，实在非常郁闷，我从MSDN订阅下载（收费的噢，杯具）则有67.6MB），如果大家实在找不到下载或者15.02MB的不可用，可以联系我。</li><br /><h3>参考链接</h3><ul><li>http://blog.csdn.net/skyremember/archive/2009/09/17/4562090.aspx</li><li>http://blog.sina.com.cn/s/blog_4758691d0100d8mc.html</li><li>http://lll332.blog.163.com/blog/static/1553692220093404635752/</li><li>http://msdn.microsoft.com/en-us/library/aa373931%28VS.85%29.aspx</li><li>http://hi.baidu.com/hhacker/blog/item/2fc5b3fb0b24132a4f4aea1d.html</li><li>http://blog.csdn.net/just_one_two/archive/2009/10/05/4634391.aspx</li><li>http://blog.csdn.net/liufei_learning/archive/2009/12/21/5047632.aspx</li><li>http://msdn.microsoft.com/zh-cn/library/958x11bc%28VS.80%29.aspx</li><li>http://bbs.xiakexing.com/cgi-bin/topic.cgi?forum=22&topic=498</li></ul><img src ="http://www.cppblog.com/mymsdn/aggbug/103863.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mymsdn/" target="_blank">volnet</a> 2009-12-23 23:47 <a href="http://www.cppblog.com/mymsdn/archive/2009/12/23/how-to-complie-TrueCrypt-63a.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>最大公约数问题</title><link>http://www.cppblog.com/mymsdn/archive/2009/03/04/75564.html</link><dc:creator>volnet</dc:creator><author>volnet</author><pubDate>Wed, 04 Mar 2009 15:52:00 GMT</pubDate><guid>http://www.cppblog.com/mymsdn/archive/2009/03/04/75564.html</guid><wfw:comment>http://www.cppblog.com/mymsdn/comments/75564.html</wfw:comment><comments>http://www.cppblog.com/mymsdn/archive/2009/03/04/75564.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mymsdn/comments/commentRss/75564.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mymsdn/services/trackbacks/75564.html</trackback:ping><description><![CDATA[<p><a href="http://www.cppblog.com/images/cppblog_com/mymsdn/WindowsLiveWriter/bcfa3ba9c083_14F7B/image_6.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/mymsdn/WindowsLiveWriter/bcfa3ba9c083_14F7B/image_thumb_2.png" width="700" height="228"></a> </p> <p></p> <p></p> <p><a href="http://www.cppblog.com/images/cppblog_com/mymsdn/WindowsLiveWriter/bcfa3ba9c083_14F7B/image_12.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/mymsdn/WindowsLiveWriter/bcfa3ba9c083_14F7B/image_thumb_5.png" width="700" height="423"></a></p> <p><a href="http://www.cppblog.com/images/cppblog_com/mymsdn/WindowsLiveWriter/bcfa3ba9c083_14F7B/image_14.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/mymsdn/WindowsLiveWriter/bcfa3ba9c083_14F7B/image_thumb_6.png" width="700" height="467"></a> </p> <p><a href="http://www.cppblog.com/images/cppblog_com/mymsdn/WindowsLiveWriter/bcfa3ba9c083_14F7B/image_16.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/mymsdn/WindowsLiveWriter/bcfa3ba9c083_14F7B/image_thumb_7.png" width="700" height="475"></a></p> <p><a href="http://www.cppblog.com/images/cppblog_com/mymsdn/WindowsLiveWriter/bcfa3ba9c083_14F7B/image_18.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/mymsdn/WindowsLiveWriter/bcfa3ba9c083_14F7B/image_thumb_8.png" width="700" height="414"></a>&nbsp; </p> <p><a href="http://www.cppblog.com/images/cppblog_com/mymsdn/WindowsLiveWriter/bcfa3ba9c083_14F7B/image_20.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/mymsdn/WindowsLiveWriter/bcfa3ba9c083_14F7B/image_thumb_9.png" width="700" height="461"></a> </p> <p><a href="http://www.cppblog.com/images/cppblog_com/mymsdn/WindowsLiveWriter/bcfa3ba9c083_14F7B/image_22.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/mymsdn/WindowsLiveWriter/bcfa3ba9c083_14F7B/image_thumb_10.png" width="700" height="458"></a> </p> <p><a href="http://www.cppblog.com/images/cppblog_com/mymsdn/WindowsLiveWriter/bcfa3ba9c083_14F7B/image_24.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/mymsdn/WindowsLiveWriter/bcfa3ba9c083_14F7B/image_thumb_11.png" width="700" height="438"></a> </p> <p><a href="http://www.cppblog.com/images/cppblog_com/mymsdn/WindowsLiveWriter/bcfa3ba9c083_14F7B/image_26.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.cppblog.com/images/cppblog_com/mymsdn/WindowsLiveWriter/bcfa3ba9c083_14F7B/image_thumb_12.png" width="700" height="251"></a> </p> <p>以上内容摘自《编程之美》P150-154。</p> <p>为了方便使用，下面是可拷贝的代码：</p> <p>Math.h</p><pre class="gc-code"><span style="color: blue">#pragma once

class </span>Math
{
<span style="color: blue">public</span>:
    Math(<span style="color: blue">void</span>);
    ~Math(<span style="color: blue">void</span>);

<span style="color: blue">public </span>:
    <span style="color: green">//编程之美P150-154

    //求最大公约数，欧几里德——辗转相除法
    </span><span style="color: blue">static int </span>Gcd1(<span style="color: blue">int </span>x, <span style="color: blue">int </span>y);

    <span style="color: green">//求最大公约数，欧几里德——辗转相除法（变相将除法变成了减法）
    </span><span style="color: blue">static int </span>Gcd2(<span style="color: blue">int </span>x, <span style="color: blue">int </span>y);

    <span style="color: blue">static int </span>Gcd3(<span style="color: blue">int </span>x, <span style="color: blue">int </span>y);

    <span style="color: blue">inline static bool </span>IsEven(<span style="color: blue">int </span>x);

    <span style="color: blue">inline static int </span>Absolute(<span style="color: blue">int </span>x);
};
</pre>
<p>Math.cpp</p><pre class="gc-code"><span style="color: blue">#include </span><span style="color: #a31515">"Math.h"

</span>Math::Math(<span style="color: blue">void</span>)
{
}

Math::~Math(<span style="color: blue">void</span>)
{
}

<span style="color: blue">int </span>Math::Gcd1(<span style="color: blue">int </span>x, <span style="color: blue">int </span>y)
{
    <span style="color: green">//y, x%y顺序不能错；
    </span><span style="color: blue">return </span>y ? Gcd1(y, x % y) : x;
}

<span style="color: blue">int </span>Math::Gcd2(<span style="color: blue">int </span>x, <span style="color: blue">int </span>y)
{
    <span style="color: green">//与Gcd1相同的方式，但由于x%y计算速度较x-y要慢，但效果相同，所以换用x - y
    // 但用减法和除法不同的是，比如和，%20=10，-20=70，也就是-4×=10
    // 也就是说迭代次数较Gcd1而言通常是增加了。
    </span><span style="color: blue">return </span>y ? Gcd1(y, x - y) : x;
}

<span style="color: blue">int </span>Math::Gcd3(<span style="color: blue">int </span>x, <span style="color: blue">int </span>y)
{
    <span style="color: blue">if</span>(x &lt; y)
        <span style="color: blue">return </span>Gcd3(y, x);
    <span style="color: blue">if</span>(y == 0)
        <span style="color: blue">return </span>x;
    <span style="color: blue">else
    </span>{
        <span style="color: blue">if</span>(IsEven(x))
        {
            <span style="color: blue">if</span>(IsEven(y))
                <span style="color: blue">return </span>(Gcd3(x &gt;&gt; 1, y &gt;&gt; 1) &lt;&lt; 1);
            <span style="color: blue">else
                return </span>Gcd3(x &gt;&gt; 1, y);
        }
        <span style="color: blue">else
        </span>{
            <span style="color: blue">if</span>(IsEven(y))
                <span style="color: blue">return </span>Gcd3(x, y &gt;&gt; 1);
            <span style="color: blue">else
                return </span>Gcd3(y, x - y);
        }
    }
}

<span style="color: blue">bool </span>Math::IsEven(<span style="color: blue">int </span>x)
{
    <span style="color: blue">return </span>!(<span style="color: blue">bool</span>)x &amp; 0x0001;
}

<span style="color: blue">int </span>Math::Absolute(<span style="color: blue">int </span>x)
{
    <span style="color: blue">return </span>x &lt; 0 ? -x : x;
}</pre>
<p>Main.cpp</p><pre class="gc-code"><span style="color: blue">#include </span><span style="color: #a31515">&lt;stdafx.h&gt;
</span><span style="color: blue">#include </span><span style="color: #a31515">&lt;iostream&gt;
</span><span style="color: blue">#include </span><span style="color: #a31515">"Math.h"

</span><span style="color: blue">using namespace </span>std;
<span style="color: blue">int </span>_tmain(<span style="color: blue">const int </span>&amp; arg)
{
    cout&lt;&lt;<span style="color: #a31515">"Math::Gcd1(42,30) = "</span>&lt;&lt;Math::Gcd1(42,30)&lt;&lt;endl;
    cout&lt;&lt;<span style="color: #a31515">"Math::Gcd1(30,42) = "</span>&lt;&lt;Math::Gcd1(30,42)&lt;&lt;endl;
    cout&lt;&lt;<span style="color: #a31515">"Math::Gcd1(50,50) = "</span>&lt;&lt;Math::Gcd1(50,50)&lt;&lt;endl;
    cout&lt;&lt;<span style="color: #a31515">"Math::Gcd1(0,0) = "</span>&lt;&lt;Math::Gcd1(0,0)&lt;&lt;endl;
    cout&lt;&lt;<span style="color: #a31515">"Math::Gcd1(-42,-30) = "</span>&lt;&lt;Math::Gcd1(-42,-30)&lt;&lt;endl;
    cout&lt;&lt;<span style="color: #a31515">"Math::Gcd1(-42,30) = "</span>&lt;&lt;Math::Gcd1(-42,30)&lt;&lt;endl;

    cout&lt;&lt;<span style="color: #a31515">"------------------------------"</span>&lt;&lt;endl;

    cout&lt;&lt;<span style="color: #a31515">"Math::Gcd2(42,30) = "</span>&lt;&lt;Math::Gcd2(42,30)&lt;&lt;endl;
    cout&lt;&lt;<span style="color: #a31515">"Math::Gcd2(30,42) = "</span>&lt;&lt;Math::Gcd2(30,42)&lt;&lt;endl;
    cout&lt;&lt;<span style="color: #a31515">"Math::Gcd2(50,50) = "</span>&lt;&lt;Math::Gcd2(50,50)&lt;&lt;endl;
    cout&lt;&lt;<span style="color: #a31515">"Math::Gcd2(0,0) = "</span>&lt;&lt;Math::Gcd2(0,0)&lt;&lt;endl;
    cout&lt;&lt;<span style="color: #a31515">"Math::Gcd2(-42,-30) = "</span>&lt;&lt;Math::Gcd2(-42,-30)&lt;&lt;endl;
    cout&lt;&lt;<span style="color: #a31515">"Math::Gcd2(-42,30) = "</span>&lt;&lt;Math::Gcd2(-42,30)&lt;&lt;endl;

    cout&lt;&lt;<span style="color: #a31515">"------------------------------"</span>&lt;&lt;endl;

    cout&lt;&lt;<span style="color: #a31515">"Math::Gcd3(42,30) = "</span>&lt;&lt;Math::Gcd3(42,30)&lt;&lt;endl;
    cout&lt;&lt;<span style="color: #a31515">"Math::Gcd3(30,42) = "</span>&lt;&lt;Math::Gcd3(30,42)&lt;&lt;endl;
    cout&lt;&lt;<span style="color: #a31515">"Math::Gcd3(50,50) = "</span>&lt;&lt;Math::Gcd3(50,50)&lt;&lt;endl;
    cout&lt;&lt;<span style="color: #a31515">"Math::Gcd3(0,0) = "</span>&lt;&lt;Math::Gcd3(0,0)&lt;&lt;endl;
    cout&lt;&lt;<span style="color: #a31515">"Math::Gcd3(-42,-30) = "</span>&lt;&lt;Math::Gcd3(-42,-30)&lt;&lt;endl;
    cout&lt;&lt;<span style="color: #a31515">"Math::Gcd3(-42,30) = "</span>&lt;&lt;Math::Gcd3(-42,30)&lt;&lt;endl;

    <span style="color: blue">return </span>0;
}</pre><a href="http://11011.net/software/vspaste"></a>
<p>不过有一点值得一提，就是所谓性能最好效率最高的Gcd3不支持负数，也就是最后两行测试代码无法通过。但是限于对负数的最大公约数并没有定义，也就是说即便上面的Gcd1和Gcd2好像算出了负数，但它们的结果没有意义。</p><img src ="http://www.cppblog.com/mymsdn/aggbug/75564.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mymsdn/" target="_blank">volnet</a> 2009-03-04 23:52 <a href="http://www.cppblog.com/mymsdn/archive/2009/03/04/75564.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Emacs 快速指南</title><link>http://www.cppblog.com/mymsdn/archive/2008/10/01/emacs-tutorial-chinese-gb.html</link><dc:creator>volnet</dc:creator><author>volnet</author><pubDate>Tue, 30 Sep 2008 19:09:00 GMT</pubDate><guid>http://www.cppblog.com/mymsdn/archive/2008/10/01/emacs-tutorial-chinese-gb.html</guid><wfw:comment>http://www.cppblog.com/mymsdn/comments/57235.html</wfw:comment><comments>http://www.cppblog.com/mymsdn/archive/2008/10/01/emacs-tutorial-chinese-gb.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/mymsdn/comments/commentRss/57235.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mymsdn/services/trackbacks/57235.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 初入Emacs，还是要入乡随俗的，基于Unix的程序还是在键盘命令上下了很大的功夫，但这样的通用性也得到了极大的提高。下面是拷贝Emacs的快速指南的中译版本，只是记录一下，没有别的意思。呵呵提供一下Windows平台Emacs的安装方式：1、下载Emacs基于Windows的安装包，我是从ftp://ftp.keystealth.org/pub/gnu/gnu/emacs/windows/ema...&nbsp;&nbsp;<a href='http://www.cppblog.com/mymsdn/archive/2008/10/01/emacs-tutorial-chinese-gb.html'>阅读全文</a><img src ="http://www.cppblog.com/mymsdn/aggbug/57235.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mymsdn/" target="_blank">volnet</a> 2008-10-01 03:09 <a href="http://www.cppblog.com/mymsdn/archive/2008/10/01/emacs-tutorial-chinese-gb.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[知识库][转载]十年MFC经历认识的Microsoft技术(ZT)</title><link>http://www.cppblog.com/mymsdn/archive/2006/12/04/15988.html</link><dc:creator>volnet</dc:creator><author>volnet</author><pubDate>Mon, 04 Dec 2006 14:05:00 GMT</pubDate><guid>http://www.cppblog.com/mymsdn/archive/2006/12/04/15988.html</guid><wfw:comment>http://www.cppblog.com/mymsdn/comments/15988.html</wfw:comment><comments>http://www.cppblog.com/mymsdn/archive/2006/12/04/15988.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mymsdn/comments/commentRss/15988.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mymsdn/services/trackbacks/15988.html</trackback:ping><description><![CDATA[
		<h2>
				<font color="#6600cc">
						<a href="/mymsdn/category/3173.html">
								<font size="3">知识库(KnowledgeLibrary)</font>
						</a>
						<font size="3"> </font>
				</font>
		</h2>
		<p>
				<strong>固定链接</strong>：<a href="/mymsdn/category/3173.html">http://www.cppblog.com/mymsdn/category/3173.html</a></p>
		<p>
		</p>
		<hr />
		<p>
				<strong>文章标题</strong>：十年MFC经历认识的Microsoft技术(ZT)<br /><strong>关键字</strong>：MFC、Microsoft、技术对比、技术发展<br /><strong>转载自</strong>：<a href="http://blog.vckbase.com/hangwire/archive/2005/05/26/5806.html">http://blog.vckbase.com/hangwire/archive/2005/05/26/5806.html</a><br /><strong>作者</strong>：hangwire <br /><strong>发表时间</strong>：2005-05-26 09:13<br /></p>
		<hr />
		<h2>一、初识MFC</h2>
		<p>　　我最初知道MFC大概是在1993年，那个时候Visual C++还没面世，当时Microsoft的C++编译器还很弱，官方的名字是Microsoft C/C++ 7.0，MFC的版本是1.0，几乎没有引起什么反响，那个时期最好的C++开发环境是Borland C++ 3.1，其实，大概是1992年11月份，一个偶然的机会，我领略到Borland公司的厉害，记不得在什么地方，我看到一个绝妙的集成开发环境，即Turbo C++ 3.0 for Windows，这是我记忆中第一个真正的Windows环境下的C++集成开发环境，那种激动的感觉至今仍记忆犹新，不客气的说，当时至少在C++方面，Microsoft与Borland不是一个水平的，Borland明显的要高于Microsoft ，Borland的产品在技术上给我留下深刻的印象。那个时候Microsoft最好的开发平台是Visual Basic 3.0，而Borland的Delphi正处于开发阶段（Delphi 的代码名称是："VB Killer"）……，想起这些十几年前的往事，我不禁感慨万千。</p>
		<p>　　十几年来，我用过许多开发环境，关于Visual Basic，我用过最早的DOS版本，Windows版的Visual Basic我基本上全都用过，至今我还记得每个版本的VB安装盘磁盘的盘数。同样，我用过各个版本的Delphi，特别是Delphi 2.0，给我留下极好的印象。Delphi提供真正编译的可视化开发环境，那个时候（1994年左右），Delphi就可以开发带有GUI的动态链接库，你可以想象，在Microsoft Access 2.0的应用程序中可以加载一个Delphi Form并进行程序交互，那种感觉真是棒极了。</p>
		<p>　　Borland C++是我心中无法抹掉的遗憾，从Turbo C到C++ Builder，我深刻的体验到Borland的辉煌和无奈，Delphi从VB Killer走到为VB护航（你可以想象Delphi一步到位的ActiveX 控件开发技术有多牛，早期的VB有多土，早期的VB不能开发动态链接库，因此无法开发ActiveX 控件，想起来真令人嘘唏不已），Borland C++的命运也是不济。Borland C++ 3.1的辉煌永远不再了，十几年的开发工作中，我在C++上投入了大量的精力，Borland C++曾经给我带来无数的激动，然而这个经典的名字却在与Microsoft的竞争中渐渐的流逝了……。</p>
		<p>　　MFC4.0的出现，使得人们感觉Microsoft在C++方面赶上来了，这一版的MFC是Win95推出后出现在Visual C++ 4中（Microsoft没有VC 3，VC4以前的版本是2.2、2.1、2.0、1.51、1.5、1.0）。也许是对Borland C++的潜意识的失望，我不知不觉的接受了MFC，VC 4.2推出时，我通过正常渠道购买了这个编译器的企业版。</p>
		<p>二、关于Microsoft</p>
		<p>　　关于Microsoft，有无数的人要对这个名字叙说感觉，这个令人讨厌的名字！不知道是喜欢还是憎恶，你是程序员，你的心思可能就要因Microsoft的存在而动，即使你用Linux，你可能也是因为Microsoft技术因素。多少年来，这个名字每天都出现在你、我、他的面前，因为你不得不面对Windows的存在，可是你憎恨这个名字吗？你讨厌这个名字吗？我不知道是否已经对这个名字麻木了。</p>
		<p>　　1998年我个人订了Microsoft MSDN Universal 版，我开始比较全面接触这个公司的开发技术，你可以想象，1998年当你面对上百张技术光盘的时候，你就知道什么叫做"厚度"，当我们有时说出"赶上"或 "达到"Microsoft某些产品的水平的时候，可能我们缺乏对这个公司"厚度"的真实了解。进入MSDN，我感觉Microsoft简直不是一个"公司"，而是（或者正在形成）一个"社会"。</p>
		<p>　　当时著名的技术网站<a href="http://www.codeguru.com/">http://www.codeguru.com</a>全部的技术资料是可下载的（那个时候<a href="http://www.codeguru.com/">http://www.codeguru.com</a>提供整个网站内容下载服务，大约3M左右），大名鼎鼎的<a href="http://www.codeproject.com/">www.codeproject.com</a>还不存在。一开始，我始终潜意识在技术上对比Microsoft与Borland，应当说技术上Borland不比Microsoft弱，即使现在也有人持有这个看法，可是为什么Borland走到今天这个地步？而Microsoft却如日中天？若干年前，这两个公司竞争何等激烈，而现在却是另一番"合作"的景象？可能很多人想过，如果Borland不存在，对Microsoft不是更有力吗？其实Microsoft可能精通中国历史，读过《三国》、十分了解战国时期的中国，其实Borland形式上的存在，对Microsoft是十分有利的，至少形式上还有竞争对手，而事实上Borland已经受控于Microsoft（Microsoft是Borland的大股东）。你可以看到一些微妙的现象：Borland为Microsoft提供了大量的人才，其中包括Delphi总设计师以及Borland C++编译器的核心成员；同时也为Microsoft .NET提供强有力的护航服务（看看C# Builder、Delphi .NET）。1998年Microsoft 的COM技术基本已经成熟，这个技术使人感到震撼，当时Microsoft的对手们提出"OpenDoc"用于对抗"COM"，你看看"OpenDoc"阵营的几个成员：IBM、Apple、Borland、Novell，你会感到这个阵营十分豪华、强大。但结果却差强人意，"OpenDoc"无疾而终，而"COM"依然生机勃勃。</p>
		<p>　　有人说"COM"没落了，那么就太不了解Microsoft了。在与"OpenDoc"的竞争中，"COM"是个彻底的胜利者，在与"Java"的竞争中，"COM"成功的进化了，在这个过程中Microsoft体现了强大的吸收能力、以及无法想象的韧劲。.NET只不过是COM的"别名"而已。对于一个经验丰富的C++程序员而言，.NET就是COM的进化，而Microsoft内部.NET就是"COM 3.0"（OLE2就是COM 2.0），而"CLR"就是一个不择不扣的COM对象。曾经有人问我，既然牛顿时代就奠定了基础（想想著名的牛顿-莱布尼茨公式），几百年后的今天，数学还研究"微积分"吗？回答当然是依然在研究！"微积分"早期是针对函数的，现代"微积分"是针对"流形（Manifold）、纤维丛（Fiber Bundle）"的，概念深奥了，可是基本思想不变，只是"微积分"的思想得到合理的延拓与进化，你了解Microsoft吗？Microsoft Research有一批超一流的数学家在为Microsoft工作，其中一些是斐尔兹奖的得主，Microsoft正在实现如同"微积分"进化到"微分流形"一样将"COM"进化到".NET"。从科学概念角度上分析COM与Java，可能COM更全面、精确，从实现的成熟度上Java可能更成熟，可是你看到，Microsoft正在不紧不慢的追赶。Microsoft令人联想起战国时期的强秦。</p>
		<p>　　战国时期的秦国，采取"远交近攻""抚弱掠强"等措施傲视六国，今天的Microsoft也是这样，VB1.0时，Microsoft推出"VBX"控件技术，众多的小公司得以生存，Microsoft自己不开发"VBX"组件，同样"VBX"进化为"OCX"时，Microsoft并不十分强大，可是这种试探得到众多小公司的响应。1997年Microsoft Office 97、1998年Microsoft推出Visual Studio 6.0，给众多中、小公司提供了生存、发展的机会，例如Microsoft Office 97中集成了Visual Basic for Application 5.0，这项技术使得几百家软件开发商与Microsoft签署了VBA技术许可协议，即使AutoDesk这样的公司都与Microsoft签署了这个协议，这个协议使得每个集成VBA的产品的给个用户许可为Microsoft付40$的许可费，如果你了解VSIP（Visual Studio Integration Protocol）协议，以及有多少公司签订了VSIP协议，你就真正感觉到Microsoft的可怕；Microsoft Office 97、Visual Studio 6.0的用户界面十分漂亮，为什么Microsoft自己的开发工具不提供类似的软件组件？你看到众多第三方的Microsoft盟友纷纷推出自己的界面库以模仿Microsoft，他们不会反对Microsoft，因为他们已经形成了使得Microsoft以及这些公司得以生存的生态圈。</p>
		<p>　　Microsoft的技术储备有多少，Microsoft之外的人很难说清楚，Microsoft中国公司也未必了解多少，1999年WTL类库刚刚出现的时候，人们就希望WTL能得到官方的支持，或授权给一个Microsoft之外的一个公司（你能想象出Borland C++ 5.0内置的ActiveX开发机制是基于Microsoft ATL类库吗？），直到今天，WTL依然如故，我们完全相信，如果Microsoft强力推广WTL，WTL完全可以流行，可是Microsoft不缺类似的技术，类似的类库还有BCL(Base Control Library，一个用于开发轻量级ActiveX控件的类库)，Microsoft还有一个基于ATL的类库，这个类库用于开发ActiveX Designer，ActiveX Designer是绝大多数程序员不了解的一类对象，如果你熟悉Office开发，你知道Office VBA 中有一类对象，即Form2，此外VB6.0 中的报表设计器（以及著名的Active Reporter），都属于此类对象，用这个类库，你可以为VB6.0以及集成VBA的系统提供定制化的可视化设计机制等等，如今ActiveX Designer已经演化为集成于Visual Studio .NET中的设计器。</p>
		<p>三、向Microsoft学习</p>
		<p>　　无论从什么角度评价Microsoft，我觉得Microsoft是值得我们学习的，如果说生活在这个时代有Microsoft存在是一场灾难，你就应该痛恨这个家伙，但你首先要向这个家伙学习！我无意为Microsoft歌功颂德，我只是想说出十几年我对Microsoft技术的感受。</p>
		<p>　　Microsoft在研究式的开发中受益极大，如果你有兴趣，你可以访问<a href="http://research.microsoft.com/">http://research.microsoft.com/</a>，虽然部分中国公司也有研究院，但与Microsoft相比，真有"米粒之珠，也放光华？"的感觉。2003年，我在北京的一个地方现场体验了Microsoft亚洲研究院的招聘会，我看到中国的精英们进入Microsoft的渴望，事实上，在中国大陆，Microsoft亚洲研究院的人力资源已经延伸到各著名高校的相关专业的核心层，我感到，Microsoft几乎不需要"求贤"，因为，只要Microsoft需要，精英们会"蜂拥而至"，每个人都有"可以理解"的理由而向往那个地方，如果为搞数学研究蜂拥到加州大学，我觉得可以理解，因为那里有数学土壤，出了成果国人也会感到自豪，因为"科学无国界"。技术是否有国界？不知道是否有定论？！想想DVD等技术专利给国内业界带来的灾难，不知道应不应该痛定思痛，在Microsoft校园招聘现场的气氛中，我似乎明白了为什么国人"原创技术"少得可怜。我读过几本Microsoft亚洲研究院的高手写的书，明显可以看出，Bill gate 是他们的精神领袖以及他们对Microsoft的虔诚，国内的研究机构应当研究一下Microsoft的用人之道，Microsoft好像是三国里的人物，不知是刘备还是曹操，或者二者的混合物。我经常路过西格玛大厦，第一次西格玛大厦进入真有"朝圣"的感觉，也与Microsoft中国的几个层次的人打过交道，各中滋味实在一言难尽。</p>
		<p>　　在Office大战中，国产软件的确在一些方面与Microsoft进行较量，其实给人的感觉很勉强，界面上的似是而非，或用户习惯方面的接近并不能解决根本的问题，一个好的软件开发人员必须是一个软件使用的高手，很难想象一个软件操作水平很拙劣的开发人员能开发出高水平的软件，我最早使用的软件之一就是Microsoft Word，当时的版本是2.0，大概是1992年的事情，给我留下深刻印象的是集成于Word中的Word Basic，后来，我接触到Excel 3.0，不出所料，Excel中集成的是Excel Basic，后来使用的Access中自然内置Access Basic 1.0，在这些软件集成捆绑成Office之前，我就感觉这些产品的构思十分了不起，很具有Microsoft的风格，因为你知道，即使是一个DOS，Microsoft都要提供一个内置的QBasic或GW Basic。虽然关于Microsoft的产品评论很多，作为一个技术人员，我认为Microsoft的产品构思绝对是第一流的，从1994年早期的Office系列到1997年形成的Office 4.2，我认为，技术构思上均领先于我国2002年以后的Office产品，你听说过如下说法吗？"Dos 作为操作系统的时代，Windows是应用软件；Windows是操作系统时，Office成为Dos时代的Windows；那么如果按此规律，Office会不会替代Windows而成为操作系统？"，现在在开发领域Visual Studio( .NET)正在成为另一个Office，你注意到了吗？控制Visual Studio( .NET)集成开发环境的仍然是一个Basic语言引擎（Visual Basic .NET）。 </p>
		<p>　　与许多公司不同的是，在技术体系上，Microsoft几乎所有的产品是息息相关的，Windows、Office、Visual Studio .NET虽然各不相同，但公共的核心即将形成，我们已经看到，核心组件方面，Office与Visual Studio .NET日渐趋于一致，例如Microsoft正在将Office 2003的核心组件VBA 6.X逐步用新的Visual Studio Tools for Office替代，而我们依然在一些似是而非的现象上与Microsoft的产品比较差距，国家采购或政府采购支持的公司，不去钻研核心技术，只是急功近利的采用短期行为急于与Microsoft相争，不知是否有蚍蜉撼树的感觉，个人的体验是，先学习Microsoft，踏踏实实的学，了解Microsoft，深入的了解，然后再喊口号。</p>
		<p>四、为什么用MFC？</p>
		<p>　　经过若干年的竞争，Borland 的OWL几乎消失了，这个OWL是个非常漂亮的C++类库，在Borland C++ 3.1风光无限的年代，OWL真正的做到了独领风骚。然而，Borland C++ 4.0错过了进入32位程序的最佳时机，BC 4.0推出后不久，迎来了Win95，Borland仓促上阵，以一个小的"Pack"使得BC4可以编译基于Win4的程序，当时的Visual C++是2.0版，支持Window16的版本为Visual C++1.51，有意思的是Borland可以用同一个编译器同时支持Win16、Win32，而Microsoft却不得不为Win16、Win32提供不同的编译器。然而，非正式版本的Visual C++ 2.1与Visual C++ 2.2却悄悄地支持了Win95的最新特征，即Win95新提供的一组公共控件，在我的印象中，Borland对Win95新特征的支持不利使得MFC与OWL的距离极大的缩短了。稍后到来的Borland C++ 4.5没有改变这个状况，尽管Borland C++ 5.0同时支持OWL与MFC，可是败象已经显露，Borland C++非常遗憾的只走到了5.5版。C++ Builder虽然形式上引入了Delphi的VCL库，可是许多C++程序员并不买账，因为许多以C++为乐的人更喜欢以编辑的模式进行编码。Visual C++ 4.0的出现，在C++这个战场上，Borland开始落败了。</p>
		<p>　　MFC发展到今天，已经十多年了，尽管褒贬不一，但可以肯定，十几年的技术积累已经奠定了MFC的生存基础，即使Microsoft的长角发布，MFC也不能推出Windows的舞台，事实上，长角（Longhorn）之后的Visual Studio .NET仍将MFC作为一个重要的组成部分，在今年的Visual Studio .NET 2005中，MFC在C++中的位置依然如故。MFC的未来，应该不必担心，只要你深入考察.NET类库，你会发现，MFC的许多思想机制正悄然进入.NET，与此同时，Microsoft的第三方盟友十多年来已为MFC开发了大量的扩展库，如果Microsoft是船，第三方盟友就是载舟之水。许多人认为MFC不发展了，其实是一种错觉，Visual C++ 6的界面十分经典，特别是其中的Docking控制条机制，其实Visual C++ 6的IDE完全就是MFC写的，可是MFC类库中控制条相关的类功能很弱，为什么？你会看到许多与Microsoft友好的公司，他们很快的在MFC基础上实现了Visual C++ 6 的Docking机制，这就是Microsoft的高明之处，Microsoft很会给盟友提供机会，其一贯的做法就是在自己的商品化产品中预先提供一些有趣的特征，使得其他一些公司进行模仿以带动用户群体。Borland不具备这样的储备。MFC第三方市场的繁荣，得益于Microsoft的策略与明智。MFC可否跨平台？理论上完全可以，Microsoft不做，也是策略，但是有许多重要的产品Microsoft却默许MFC移植到其他平台，事实上，Microsoft的合作伙伴之一Mainsoft公司（Windows源码就是从这家公司流失的），几年来就是负责移植MFC程序移植到UINIX、Linux、AIX等操作系统之上。</p>
		<p>　　新版的Visual C++中MFC已经支持.NET开发了，MFC与ATL的协作更好了。根据我的经验，MFC、ATL与.NET库三者完全可以融合在一起综合应用到实际的开发工作中去，如果你是MFC行家，我希望ATL与.NET库能成为你的忠实的左右手。那么有没有同时支持MFC、ATL与.NET库的程序？当然有，Visual Studio .NET IDE就是！而且Visual Studio .NET IDE还支持用ATL与.NET库扩展的Addin，如果你希望用MFC管理ATL与.NET库，请继续支持我！</p>
		<p>五、认识Application对象</p>
		<p>　　如果你熟悉Microsoft Office，你应该进一步的剖析这个大型软件，Microsoft Office中几乎每个程序都是可二次开发的，这一点得益于Microsoft Office内置的二次开发机制，一个是基于COM机制的VBA模型，另一个是基于.NET框架的托管模型：Visual Studio Tools for Office。作为一名程序员，你应当在技术角度解析Office的技术结构。Microsoft的大多数软件的对象结构可以通过Visual Studio提供的工具OLE/COM Object Viewer考察其类型库得到，通过引用类型库，你甚至可以得到描述对象信息的C++头文件。这样做真是好处多多。一个典型的Office通常都有一个Application对象（或其他一个与之相当的对象），这个对象相当于软件枢纽，在这里，我们不讨论Office，借此话题说说Application对象。大多数支持扩展（Addin、Plugin）的软件都存在类似的构造。通常，一个系统得Application对象或者是一个COM对象，或者是一个.NET对象，如果你的系统存在这类对象，你的系统就基本具备支持Addin、Plugin的机制了。一个理想的做法就是在一个MFC系统中，内置一个ATL对象或.NET对象，稍后我们给出方案如何做到这一点。设计Application对象的关键是如何规划这个对象的属性、方法、事件。如果你希望系统具备良好的扩展性，Application对象是十分关键的，这也是构架艺术的体现。所谓Addin(Plugin)，是系统运行时根据需要加载的对象库，Addin(Plugin)之所以可以扩展系统，关键的因素就是系统加载Addin(Plugin)时，将Application对象传递给Addin(Plugin)库，设想一下，如果Application恰到好处的触发了系统事件，而Addin(Plugin)库如愿的解释了事件，一个Addin(Plugin)库的任务不就OK了吗！因此Application对象是系统设计的关键。</p>
		<p>　　如果你精通ATL对象，在你的MFC系统中添加一个ATL对象，这个任务可以用VC Wizard完成。你已经接受了一个事实，就是MFC程序中存在一个CXXXApp对象（CWinApp的派生类），现在你要做的是增加一个对应得ATL对象。这个对象可以在CXXXApp::InitInstance()中创建，如果ATL对象的类是CXXXAppObject，建议你在CXXXApp对象对象中增加一个成员变量，例如：CComObject&lt;CXXXAppObject&gt;* m_pAppObj，然后可以入下初始化m_pAppObj：</p>
		<p>m_pAppObj = new CComObject&lt;CXXXAppObject&gt;；</p>
		<p>　　注意程序结束时在CXXXApp::ExitInstance()中释放m_pAppObj，语句如下：</p>
		<p>delete m_pAppObj；</p>
		<p>　　你可以将系统得关键属性设置成CXXXAppObject的属性，例如系统得标题、是否为多文档等等。系统希望外部调用的功能可以实现为CXXXAppObject的方法，这一点取决于你的需要。系统需要外部扩展的功能，表现为CXXXAppObject的事件，关键是在恰当的位置触发事件以及提供的事件参数。例如，你可以在CXXXApp::InitInstance()触发应用程序开始的事件OnStartUp，Plugin捕获事件后，可以进行特定的初始化（身份确认、初始信息查询等等）；你可以在CXXXApp::ExitInstance()触发应用程序结束事件，Plugin捕获事件后，处理用户需要的系统退出工作。所有的设计取决于具体设计。</p>
		<p>　　如何加载Plugin，是一个有趣的问题，如果Plugin实现为一个COM范畴（Category），可以运用COM技术枚举这个Category；可以将Plugin安装到一个特定目录，也可以通过注册表。Plugin的实现可以用COM技术、也可以用.NET框架。适当的机会我会提供例子……</p>
		<p>六、后记</p>
		<p>　　一时心血来潮，就写了这篇文章，很难说是有心，还是无意。几天前我在新浪网上看应氏杯围棋决赛，我觉得该赢了吧，作为一个围棋迷，我们等了十几年，等到了属于国人的应氏杯。记得7、8年前在还在大学工作的时候，有一次，一位同事兴致冲冲的走道我面前对我说："嗨，昨天马XX赢了李昌镐！"，当时我在系办公室正在看报纸，那位仁兄见我头都没抬，非常不满的抢下报纸，对我吼道："喂！马XX赢了李昌镐！！你听到没有！！！"，我对他说："你大惊小怪个啥？！马XX输了李昌镐多少盘，你知道吗？"，马XX几乎一直在输给李昌镐，人们已经不奇怪了，偶尔赢一次，国人就把他捧得北都找不到了，李昌镐弱冠17的时候就傲视这个世界了，可至今面孔不变，几天前的农心杯，中日联军5个人，被他打个落花流水，李昌镐是公认的世界第一，以至于有的高手知道下一个对手如果是他，就会去订回程机票。这次应氏杯，国人竟然感谢崔哲瀚，何也？因为这个弱冠19的小子，挡住了他的大哥李昌镐才使得应氏杯有了悬念。当国人媒体在说韩国仅李昌镐一人厉害的时候，不知道是出何居心还是自欺欺人，李昌镐年方30，不知道要力压中、日多少年！面对这个名字，真有点麻木了，这个太极虎！</p>
		<p>　　软件界又来了我们一向不齿的印度虎，2001年我们的软件出口额仅是印度的四十分之一，我们震惊了，怎么可能呢？这个四十分之一水分很大，很可能更可怜！当时我在大连参加一个关于"大连软件出口国内第一"的官方会议，那位大人在会上说："据说，我们大连软件出口国内排名第一，市有关领导希望今天的会议给出这个第一的数字依据，希望你们把数据报上来，去年的数据也可申报，注意，我们要的只是数据，你们仔细体会，我们根据数据，有奖励，机会难得呀！"……。某一天，几个朋友在我家看央视的对话节目，对话一方为国内的软件大鳄们（用友、阿尔派等公司的老总们），另一方为印度软件的一个代表团。当问及中、印软件差距的时候，我们的刘老总（代表阿尔派）不以为然的说，据他的看法，我们已经快赶上（印度）了，……，言下之意颇有印度的水平不过如此的感觉，印度方的话我至今记忆犹新："是否赶上，国际市场说的算！在中国看来，印度程序员的个性不足，技术也不怎么样，其实是个错觉，印度软件首先注重个性，许多重要的美国商品化软件都是在印度本土开发的……"，我们的舆论总是将印度程序员的水平描述的平庸至极，可是差距日渐拉开，……，围棋、足球（不好意思谈，谈不出口！）、软件，我们被近邻严酷的封锁了，乐坏了记者们、给媒体带来了生机…… </p>
		<p>　　日本江户时代的围棋，如果一个人要想世袭一个称号（例如：本因坊），他必须战胜所有的师兄弟，然后，住进师父家的内室，你知道以后的事情吗？以后，这个棋手，就得为师父一家做饭、带孩子、搞卫生……，其余的门人则一心一意的下棋，这样的人、方式，造就了一代一代的本因坊，他们的棋谱大多数都流芳至今，这就是早期日本围棋的悟道模式。软件总共有多少语句？我最早接触的计算机软件教材是一本英文版的（影印的D版），不同于我们，那本书的作者构造了"X-语言"，他们不讲什么C、Pascal、Basic，一旦缺了什么机制，就给"X-语言"添加些成分。什么C、Pascal、Basic，你感觉差不多，但现在却分出了等级！我们驾驭语言的能力弱得很，可是我们在语言的细微之处却很讲究，不知道对不对，许多程序员也许是出于虚荣而用C++，事实上，地球人到知道，做数据库，Delphi、VB远比C++胜任，铺天盖地的C++的书，写的东西几乎雷同，因为，有用的或者作者不写、或者作者不懂。有时我在想，如果国内没有内需，会怎样？也许软件内需的存在，造就了中国软件的特色，我认为国内业界并没有充分利用中国软件内需的存在，也许中国软件内需的存在是软件落后的硬伤。</p>
		<p>　　我记得一部电影《神辫》，那个英雄的大辫子被洋人炸掉了，最终他成了神枪手，战胜洋人用大刀、秘籍是不行的，用洋的东西战胜洋的技术才是正道。我觉得，一个好的程序员必须了解软件的历史，学习历史，你知道你为什么弱，别人是如何强大的。我们正在另一个战场上抗美（可笑的是我们却要赶超印度！），无论Microsoft、Borland如何争斗，无论他们谁统治谁，他们不影响美国的强大，朋友们，学习Microsoft，开发出让国人感到牛的软件！</p>
<img src ="http://www.cppblog.com/mymsdn/aggbug/15988.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mymsdn/" target="_blank">volnet</a> 2006-12-04 22:05 <a href="http://www.cppblog.com/mymsdn/archive/2006/12/04/15988.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>