??xml version="1.0" encoding="utf-8" standalone="yes"?>久久青青草原精品影院,青春久久,久久午夜免费视频http://www.shnenglu.com/mymsdn/category/3173.htmlMyMSDN记录开发新知道zh-cnWed, 07 Apr 2010 20:07:21 GMTWed, 07 Apr 2010 20:07:21 GMT60[译]高效使用auto_ptrhttp://www.shnenglu.com/mymsdn/archive/2010/04/07/Using-auto_ptr-Effectively.htmlvolnetvolnetWed, 07 Apr 2010 11:08:00 GMThttp://www.shnenglu.com/mymsdn/archive/2010/04/07/Using-auto_ptr-Effectively.htmlhttp://www.shnenglu.com/mymsdn/comments/111882.htmlhttp://www.shnenglu.com/mymsdn/archive/2010/04/07/Using-auto_ptr-Effectively.html#Feedback2http://www.shnenglu.com/mymsdn/comments/commentRss/111882.htmlhttp://www.shnenglu.com/mymsdn/services/trackbacks/111882.html本文来自C/C++用户日志Q?7Q?0Q,1999q?0?/i>  原文链接

大部分h都听说过auto_ptr指针Q但是ƈ非所有h都每天用它。不使用它是不明智的Q可ȝQ,因ؓauto_ptr的设计初hZ解决C++设计和编码的普遍问题Q将它用好可以写出更健壮的代码。本文指出如何正用auto_ptr以ɽE序变得安全Q以及如何避开危险Q而不是一般用auto_ptr的恶习所致的创徏间歇性和难以诊断的问题?

Z么它是一个“自动”指?

auto_ptr只是许许多多指针中的一U。许多商业库提供许多更强大的指针Q可以完成更多的事情。从可以理引用计数到提供更先进的代理服务等。应该把auto_ptr认ؓ是智能指针中的福特Escort[注释]Q一个基于简单且通用目的的智能指针,既没有小发明也没有丰富的Ҏ目的更不需要高性能Q但是能许多普通的事情做好Qƈ且能够适合日常使用的智能指针?

auto_ptr做这样一件事Q拥有一个动态分配内存对象,q且在它不再需要的时候行自动清理的职责。这里有个没有用auto_ptr指针的不安全的例子:

    // Example 1(a): Original code
    //
    void f()
    {
      T* pt( new T );

      /*...more code...*/

      delete pt;
    }

我们每天都像q样写代码,如果f()只是一个三行程序,也没做什么多余的事情Q这样做当然可以很好工作。但是如果f()没有执行delete语句Q比如程序提前返回(returnQ了Q或者在执行的时候抛出异怺Q然后就D已经分配的对象没有被删除Q因此我们就有了一个经典的内存泄漏?

一个ExampleQ?Q安全的办法是用一个“智能”的指针拥有q个指针Q当销毁的时候,删除那个被指的自动分配的对象。因个智能指针被单地用ؓ自动对象Q这是Q当它离开它的作用域的时候自动销毁对象)Q所以它被称作“自动”指针?

    // Example 1(b): Safe code, with auto_ptr
    //
    void f()
    {
      auto_ptr<T> pt( new T );

      /*...more code...*/

    } // cool: pt's destructor is called as it goes out
      // of scope, and the object is deleted automatically

现在q段代码不会再T对象上发生泄漏了Q不必在意这个方法是正常退是异帔R出,因ؓpt的析构函数将L在堆栈弹出的时候被调用。清理工作将自动q行?/p>

最后,使用auto_ptr和用内建指针一样地ҎQ如果要“收回”资源ƈ且再ơ手动管理的话,我们可以调用release()Q?/p>

    // Example 2: Using an auto_ptr
    //
    void g()
    {
      T* pt1 = new T;
      // right now, we own the allocated object

      // pass ownership to an auto_ptr
      auto_ptr<T> pt2( pt1 );

      // use the auto_ptr the same way
      // we'd use a simple pointer
      *pt2 = 12;       // same as "*pt1 = 12;"
      pt2->SomeFunc(); // same as "pt1->SomeFunc();"

      // use get() to see the pointer value
      assert( pt1 == pt2.get() );

      // use release() to take back ownership
      T* pt3 = pt2.release();

      // delete the object ourselves, since now
      // no auto_ptr owns it any more
      delete pt3;

    } // pt2 doesn't own any pointer, and so won't
      // try to delete it... OK, no double delete

最后,我们可以使用auto_ptr的reset()Ҏauto_ptr重置向另一个对象。如果auto_ptr已经获得一个对象,q个q程像是它先删除已l拥有的对象Q因此调用reset()Q就像是先销毁了auto_ptrQ然后重Z一个新的ƈ拥有该新对象Q?

    // Example 3: Using reset()
    //
    void h()
    {
      auto_ptr<T> pt( new T(1) );

      pt.reset( new T(2) );
        // deletes the first T that was
        // allocated with "new T(1)"

    } // finally, pt goes out of scope and
      // the second T is also deleted

包装指针数据成员

同样Qauto_ptr也可以被用于安全地包装指针数据成员。考虑下面使用Pimpl idiomQ或者,~译器防火墙Q的例子Q?sup>[1]

    // Example 4(a): A typical Pimpl
    //

    // file c.h
    //
    class C
    {
    public:
      C();
      ~C();
      /*...*/
    private:
      class CImpl; // forward declaration
      CImpl* pimpl_;
    };

    // file c.cpp
    //
    class C::CImpl { /*...*/ };

    C::C() : pimpl_( new CImpl ) { }
    C::~C() { delete pimpl_; }

单地_是C的私有细节被实现Z个单独的对象Q藏匿于一个指针之中。该思\要求C的构造函数负责ؓ隐藏在类内部的辅助“Pimpl”对象分配内存,q且C的析构函数负责销毁它。用auto_ptrQ我们会发现q非常容易:

    // Example 4(b): A safer Pimpl, using auto_ptr
    //

    // file c.h
    //
    class C
    {
    public:
      C();
      /*...*/
    private:
      class CImpl; // forward declaration
      auto_ptr<CImpl> pimpl_;
    };

    // file c.cpp
    //
    class C::CImpl { /*...*/ };

    C::C() : pimpl_( new CImpl ) { }

现在Q析构函C需要担心删除pimpl_指针了,因ؓauto_ptr自动处理它。事实上Q如果没有其它需要显式写析构函数的原因,我们完全不需要自定义析构函数。显Ӟq比手动理指针要容易得多,q且对象所有权包含q对象是一个不错的习惯Q这正是auto_ptr所擅长的。我们将在最后再ơ回这个例子?

所有权Q源Q以及调用?Sinks)

它本w很漂亮Qƈ且做得非常好Q从函数传入或传?span style="font-family: 'Courier New'" class="Code">auto_ptrsQ是非常有用的,比如函数的参数或者返回倹{?/p>

让我们看看ؓ什么,首先我们考虑当拷贝auto_ptr的时候会发生什么:一个auto_ptr获得一个拥有指针的对象Qƈ且在同一旉只允许有一个auto_ptr可以拥有q个对象。当你拷贝一个auto_ptr的时候,你自动将源auto_ptr的所有权Q传递给目标auto_ptrQ如果目标auto_ptr已经拥有了一个对象,q个对象先被释放。在拯完之后,只有目标auto_ptr拥有指针Qƈ且负责在合适的旉销毁它Q而源被讄为空QnullQ,q且不能再被当作原有指针的代表来使用?

例如Q?/p>

    // Example 5: Transferring ownership from
    //            one auto_ptr to another
    //
    void f()
    {
      auto_ptr<T> pt1( new T );
      auto_ptr<T> pt2;

      pt1->DoSomething(); // OK

      pt2 = pt1;  // now pt2 owns the pointer,
                  // and pt1 does not

      pt2->DoSomething(); // OK

    } // as we go out of scope, pt2's destructor
      // deletes the pointer, but pt1's does nothing

但是要避免陷阱再ơ用已l失L有权的auto_ptrQ?

    // Example 6: Never try to do work through
    //            a non-owning auto_ptr
    //
    void f()
    {
      auto_ptr<T> pt1( new T );
      auto_ptr<T> pt2;

      pt2 = pt1;  // now pt2 owns the pointer, and
                  // pt1 does not

      pt1->DoSomething();
                  // error! following a null pointer
    }

谨记于心Q我们现在看看auto_ptr如何在源和调用者之间工作。“源”这里是指一个函敎ͼ或者其它创Z个新资源的操作,q且通常移交出资源的所有权。一个“调用者”函数反转这个关p,也就是获得已l存在对象的所有权Qƈ且通常q负责释攑֮Q。而不是有一个源和调用者,q回q且利用一个秃头指针(译者注Q而不是用一个局部变量来传递这个指针)Q虽Ӟ通过一个秃头指针来获得一个资源通常很好Q?

    // Example 7: Sources and sinks
    //

    // A creator function that builds a new
    // resource and then hands off ownership.
    //
    auto_ptr<T> Source()
    {
      return auto_ptr<T>( new T );
    }

    // A disposal function that takes ownership
    // of an existing resource and frees it.
    //
    void Sink( auto_ptr<T> pt )
    {
    }

    // Sample code to exercise the above:
    auto_ptr<T> pt( Source() ); // takes ownership

注意下面的微妙的变化Q?

  1. Source()分配了一个新对象q且以一个完整安全的方式它q回l调用者,q让调用者成为指针的拥有着。即使调用者忽略了q回|昄Q如果调用者忽略了q回|你应该从来没有写q代码来删除q个对象Q对吧?Q,分配的对象也被自动安全地删除?/p>

    在本文的最后,我将演示q回一个auto_ptr是一个好习惯。让q回值包裹进一些东西比如auto_ptr通常是得函数变得强健的有效方式?

  2. Sink()通过传值的方式获得对象所有权。当执行完Sink()的时候,当离开作用域的时候,删除操作被执行Q只要Sink()没有所有权转移Q。上面所写的Sink()函数实际上ƈ没有对参数做M事情Q因此调用“Sink(pt);”就{于写了“pt.reset(0);”,但是大部分的Sink函数都将在释攑֮之前做一些工作?

不可以做的事情,以及Z么不能做

谨记Q千万不要以我之前没有提到的方式使用auto_ptrs。我已经看见q很多程序员试着用其他方式写auto_ptrs像他们在用其它对象一栗但问题是auto_ptrq不像其他对象。这里有些基本原则,我将把它们提出来以引起你的注意:

For auto_ptr, copies are NOT equivalent. Q复制auto_ptr与原来的不相等Q?

当你试着在一般的代码中?span style="font-family: 'Courier New'" class="Code">auto_ptrs的时候,它将执行拯Qƈ且没有Q何提C,拯是不相等的(l果Q它实是拯Q。看下面q段代码Q这是我在C++新闻l经常看见的Q?

    // Example 8: Danger, Will Robinson!
    //
    vector< auto_ptr<T> > v;

    /* ... */

    sort( v.begin(), v.end() );

在标准容器中使用auto_ptrsL不安全的。一些h可能要告诉你Q他们的~译器或者类库能够很好地~译它们Q而另一些h则告诉你在某一个流行的~译器的文档中看到这个例子,不要听他们的?

问题?span style="font-family: 'Courier New'" class="Code">auto_ptrq不完全W合一个可以放q容器类型的前提Q因为拷?span style="font-family: 'Courier New'" class="Code">auto_ptrs是不{h的。首先,没有M东西说明Qvector不能军_增加q制造出“扩展”的内部拯。再ơ,当你调用一个一般函数的时候,它可能会拯元素Q就像sort()那样Q函数必L能力假设拯是等L。至一个流行的排序拯“核心”的元素Q如果你试着让它?span style="font-family: 'Courier New'" class="Code">auto_ptrs一起工作的话,它将拯一份“核心”的auto_ptr对象Q因此{UL有权q且所有权转移l一个时对象)Q然后对其余的元素也采取相同的方式(从现有成员创建更多的拥有所有权的auto_ptrQ,当排序完成后Q核心元素将被销毁,q且你将遇到一个问题:q组序列里至一个auto_ptrQ也是刚才被掉包的那个核心元素Q不再拥有对象所有权Q而那个真实的指针已经随着临时对象的销毁而被删除了!

于是标准委员会回退q希望做一些能够帮助你避免q些行ؓ的事情:标准的auto_ptr被故意设计成当你希望在用标准容器的时候用它时打断你Q或者,臛_Q在大部分的标准库实C打断你)。ؓ了达到这个目的,标准委员会利用这样一个技巧:?span style="font-family: 'Courier New'" class="Code">auto_ptr's的拷贝构造函数和赋值操作符的右|rhsQ指向非帔R。因为标准容器的单元素insert()函数Q需要一个常量作为参敎ͼ因此auto_ptrs在这里就不工作了。(译者注Q右g能赋值给非常量)

使用const auto_ptr是一个好习惯

一个auto_ptr设计?span style="font-family: 'Courier New'" class="Code">const auto_ptrs不再丢失所有权Q拷贝一个const auto_ptr是违法的Q译者注Q没有这L构造函敎ͼQ实际上你可以针对它做的唯一事情是通过operator*()或者operator->()解引用它或者调用get()来获得所包含的指针的倹{这意味着我们有一个简单明了的风格来表达一个绝不丢失所有权的auto_ptrQ?

    // Example 9: The const auto_ptr idiom
    //
    const auto_ptr<T> pt1( new T );
        // making pt1 const guarantees that pt1 can
        // never be copied to another auto_ptr, and
        // so is guaranteed to never lose ownership

    auto_ptr<T> pt2( pt1 ); // illegal
    auto_ptr<T> pt3;
    pt3 = pt1;              // illegal
    pt1.release();          // illegal
    pt1.reset( new T );     // illegal

q就是我要说的cosntQ因此如果现在你要向世界证明你的auto_ptr是不会被改变q且L删除其所有权Q加上const是你要做的。const auto_ptr风格是有用的Q你必须它谨记于心?

auto_ptr以及异常安全

最后,auto_ptr对写出异常安全的代码有时候非常必要,思考下面的代码Q?/p>

    // Example 10(a): Exception-safe?
    //
    String f()
    {
      String result;
      result = "some value";
      cout << "some output";
      return result;
    }

该函数有两个可见的作用:它输Z些内容,q且q回一个String。关于异常安全的详细说明出了本文的范围[2]Q但是我们想要取得的目标是强异常安全的保障Q归lؓ保函数的原子性——如果有异常Q所有的作用一起发生或者都不发生?

虽然在例10(a)中的代码非常_yQ看h相当接近于异常安全的代码Q但仍然有一些小的瑕疵,像下面的客户代码所C:

    String theName;
    theName = f();

因ؓl果通过D回,因此String的拷贝构造函数将被调用,而拷贝赋值操作符被调用来结果拷贝到theName中。如果Q何一个拷贝失败了Qf()完成了所有它的工作以及所有它的Q务(q很好)Q但是结果是无法挽回的(哎哟我的妈呀Q?

我们可以做的更好吗,是否可以通过避免拯来避免这个问题?例如Q我们可?让函数有一个非帔R引用参数q向下面q样q回|

    // Example 10(b): Better?
    //
    void f( String& result )
    {
      cout << "some output";
      result = "some value";
    }

q看h很棒Q但实际不是q样的,q回result的赋值的函数只完成了一个功能,而将其它事情留给了我们。它仍然会出错。因此这个做法不可取?

解决q个问题的一个方法是q回一个指向动态分配指针的String对象Q但是最好的解决Ҏ是让我们做的更多Q返回一个指针包含在auto_ptrQ?

    // Example 10(c): Correct (finally!)
    //
    auto_ptr<String> f()
    {
      auto_ptr<String> result = new String;
      *result = "some value";
      cout << "some output";
      return result;  // rely on transfer of ownership;
                      // this can't throw
    }

q里是一个技巧,当我们有效隐藏所有的工作来构造第二个功能Q返回|当确保它可以被安全返回给调用者ƈ且在W一个功能(打印消息Q完成的时候没有抛出操作。我们知道一旦cout完成Q返回值将成功交到调用者手中,q且无论如何都会正确清理Q如果调用者接受返回|调用者将得到q个拯的auto_ptr临时对象的所有权Q如果调用者没有接受返回|也就是忽略返回|分配的String在临时auto_ptr被销毁的时候自动清理。这U安全扩展的代h呢?像我们l常实现的强异常安全一P强安全通常消耗一些效率(通常比较)——这里指额外的动态内存分配。但是当我们在效率和正确性之间做出选择的话Q我们通常会选择后者!

让我们养成在日常工作中用auto_ptr的习惯。auto_ptr解决了常见的问题Qƈ且能够你的代码变得更安全和健壮Q特别是它可以防止内存泄漏以及确保强安全。因为它是标准的Q因此它在不同类库和q_之间是可UL的,因此无论你在哪里使用它,它都是对的?

致谢

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.

注释

  1. Pimpl风格可以有效减少目构徏旉Q因为它在CU有部分改变的时候,L客户代码引vq泛的重新编译。更多关于Pimpl风格以及如何部v~译器墙Q参考这?a href="xc++.htm">Exceptional C++的条?6?0。(Addison-Wesley, 2000Q?

  2. See the article originally published in C++ Report and available on the Effective C++ CD (Scott Meyers, Addison-Wesley, 1999) and Items 8 to 19 in Exceptional C++ (Herb Sutter, Addison-Wesley, 2000).



volnet 2010-04-07 19:08 发表评论
]]>
[转]从C++的Return Value Optimization (RVO)到C#的value typehttp://www.shnenglu.com/mymsdn/archive/2010/04/06/111777.htmlvolnetvolnetTue, 06 Apr 2010 11:42:00 GMThttp://www.shnenglu.com/mymsdn/archive/2010/04/06/111777.htmlhttp://www.shnenglu.com/mymsdn/comments/111777.htmlhttp://www.shnenglu.com/mymsdn/archive/2010/04/06/111777.html#Feedback0http://www.shnenglu.com/mymsdn/comments/commentRss/111777.htmlhttp://www.shnenglu.com/mymsdn/services/trackbacks/111777.html先看一D늮单的C++代码Q?
Type get(int I){
    return Type(i);
} 

Type t = get(1); 

q里Q?我们从C++的基本语义看上去Q?应该是Type(i) 调用一ơ拷贝构造函敎ͼ 在堆栈中生成一个时对象;然后Q用该对象构造返回对象;然后对这个时对象调用析构函敎ͼ在调用者方Q?用返回的临时对象调用拯构造函C初始化对象t, q回对象的析构函数在q之后, 函数q回之前调用?

所以, Type t = get(i); 应该有三个拷贝构造函数和两个析构函数的调?

可是Q?q有一U说法是Q?~译器可能会对这两个临时对象q行优化Q最l的优化l果会是只有一ơ的构造函数。因为很明显地可以看刎ͼ q里我们其实只是要用一个整数构造一个Type对象?

? g很有道理Q?

那么Q?哪一U说法对呢? 没有调查没有发a权,于是本h用VC++6.0做了实验?放了些cout<<?.在拷贝构造函数里Q观察打印的l果Q?l果却是跟我的simple, naïve的预一致。三个拷贝构造函敎ͼ 两个析构函数?

“你个弱智编译器Q脑袋进水了吧??忘了~译器没脑袋?“很明显在这个例子里我的两个临时对象都没有用的啊Q?

于是Q上|, 查资料, google一下吧Q?

下面是我查到的一些结果:

其实Q?q种对g递的优化的研IӞ q不只局限于q回倹{对下面q个例子Q?

void f(T t) { } 
void main(void){ 
    T t1;
    f(t1); 
} 

也有q种考虑?

f(T)是按g递的。语义上应该做一个复Ӟ 使得函数内部对T的改变不会媄响到原来的t1.

但是Q因为在调用f(t1)之后Q?我们没有再用t1(除了一个隐含的destructor调用)Q是否可能把复制优化掉, 直接使用t1呢?q样可以节省掉一个拷贝构造函数和一个析构函数?

可是Q?不论是对q回值的优化Q?q是对上面这U局部对象的优化Q在1995q的C++新标准草案出台前都是为标准所严格限制?(虽然有些~译器ƈ没有遵行q个标准Q?q是支持了这U“优化?

那么Q?q又是ؓ什么呢Q?

q里面涉及到一个普遍的对side-effect的担忧?

什么又是side-effect呢?

所谓side-effect是一个函数的调用与否能够对系l的状态造成区别?

int add(int i, int j){ return i+j; }是没有side-effect的,?

void set(int* p, int I, int v){ p[I]=v; }是有side-effect的。因为它改变了一个数l元素的| 而这个数l元素在函数外是可见的?

通常意义上来_ 所有的优化应该在不影响E序的可观察行ؓ的基上进行的。否则,快则快了Q?l果却和所惌的完全不同!

而C++的拷贝构造函数和析构函数又很多都是有side-effect的。如果我们的“优化”去掉了一个有side-effect的拷贝构造函数和一个析构函敎ͼ q个“优化”就有可能改变程序的可观察行为。(注意Q?我这里说的是“可能”,因ؓ“负负得正”, 两个有side-effect的函数的调用Q?在不考虑q行q行的情况下Q?也许反而不会媄响程序的可观察行为。不q, q种塞翁失马的事儿, ~译器就很难判断了)

Zq种忧虑, 1995q以前的标准Q?明确止对含有side-effect的拷贝构造函数和析构函数的优化。同Ӟ q有一些对C++扩充的提议, 考虑让程序员自己对类q行允许优化的声明?E序员可以明地告诉~译器:不错Q?我这个拷贝构造函敎ͼ 析构函数是有side-effect, 但你别管Q?管优化Q?Z事有我呢Q?

哎, side-effect真是一个让人又恨又q东西Q它使编译器的优化变得困难;加大了程序维护和调试的难度。因?functional language 把side-effect当作z水猛兽一Pq脆止。但同时Q我们又很难dside-effect. 不说E序员们更习惯于imperative 的编E方? 象数据库操作QIO操作都天然就是side-effect.

不过Q个是认为C++标准对“优化”的保守态度是有道理的。无论如何,让“优化”可以潜在地偷偷地改变程序的行ؓL让h惌v来就不舒服的?

但是Q?矛盾是对立统一的。(惛_q俺马列可得了八十多分呢Q?对这Uaggressive的“优化”的呼声是一高q一?以Stan Lippeman为首的一撮固分子Ҏ准的颠覆和和qx变的阴谋从来没有停止过?q不Q在1996q的一个风雨交加的夜晚Q?一个阴险的C++新标准草案出炉了。在q个草案里, 加入了一个名为RVO (Return Value Optimization) 的放宽对优化的限Ӟ 妄图走资本主义道路, l资本家张目的提案。其具体内容是_允许~译器对命名q的局部对象的q回q行优化Q?即拯构造函?析构函数有side-effect也在所不惜。这个提议背后所隐藏的思想是Qؓ了提高效率, 宁可冒改变程序行为的风险。宁要资本主义的苗, 不要C会M的草了!

我想Q?q样的一个罪大恶极的提案竟会被提交,应该是因为C++的值拷贝的语义的效率实在太“妈妈的”了?当你写一?Complex operator+(const Complex& c1, const Complex& c2);的时候, 竟需要调用好几次拯构造函数和析构函数Q同志们Q(沉痛圎ͼ 语重心长圎ͼC会M的生产关pȝ优越性怎么体现啊?

接下来, 当我想Google C++最新的标准Q?看RVO是否被最l采UxQ?却什么也找不C?到ANSI的网站上去, 居然要付钱才能DOWNLOAD文?“老子在城里下馆子都不付钱Q?down你几个烂文q要l钱Q!?

故事没有l局Q?实在是不爽?也不知是不是因ؓ标准q没有敲定, 所以VC++6 没有优化, q是VCҎ没完全遵守标准?

不过Q有一Ҏ肯定的?当写E序的时候, 最好不要依赖于RVO (有hQ?象Stan Lippeman, 又叫它NRV优化)?因ؓQ?不论Ҏ准的争论是否已经有了l果Q?实际上各个编译器的实Cq是各自为政Q?没有l一?一个叫SCOtt Meyers的家?忘了是卖什么的?pQ?如果你的E序依赖于RVO, 最好去掉这U依赖。也是_ 不管RVO到底标准不标准, 你还是不能用?不仅不能用, q得时刻警惕着RVO可能带来的程序行Z的变化?Q也不知q帮家伙瞎忙了半天到底ؓ啥!Q?

说到q里Q?倒想起了C#里一个困惑了我很久的问题。记得读C#的specification的时候, 非常不解Z么C#不允许给value type 定义析构函数?

q里Q?先简略介l一下C#里的value type (原始数据cdQ?struct cd)?

在C#里的value_typep是| 永远只能copy, 取倹{因此, 它永q是in-place的。如果你把一个value type的数据放在一个对象里Q它的生命期和那个对象相同Q如果你声明一个value type 的变量在函数中, 它的生命期就在lexical scope里?

{

  The_ValueType value;

}//value 到这里就死菜?

啊呀呀Q?q不正是我们怀늚C++的stack object吗?

在C++里,Auto_ptr, shared_ptr, 容器们, 不都是利用析构函数来理资源的吗Q?

C#QJava 虽然利用garbage collection技术来攉无用对象Q?使我们不用再担心内存的回收?但garbage collectionq不保证无用对象一定被攉Q?q不保证Dispose()函数一定被调用Q?更不保证一个对象什么时候被回收?所以对一些非内存的资源, 象数据库q接Q?|络q接Q?我们q是希望能有一个类gsmart pointer的东西来帮我们管理啊。(try-finally 虽然可以用, 但因为它影响到lexical scope, 有时用v来不那么方便Q?

于是Q?我对C#的取消value type的析构函数充满了深厚的阶U仇恨?

不过Q?现在xQ?C#的这U设计一定是惩于C++p|的教训:

   1. value type 没有拯构造函数。C#只做~省copy, 没有side-effect
   2. value type 不准有析构函数。C#有garbage collection, 析构函数的唯一用途只会是做一些side-effect象关闭数据库q接?所以取消了析构函数Q?取消了value type的side-effect.
   3. 没有了side-effect, pȝ可以L地做优化?

对以下程序:

The_Valuetype get(int I){return The_Valuetype(i);}

The_Valuetype t = get(1);

在C#里我们可以快乐地_只调用了一ơ构造函数?再没有side-effect的沙漠, 再没有难以优化的荒原Q?smart pointer望而却步, 效率之花处处开遍?I have a dream, …?/p>

转蝲自:http://gugu99.itpub.net/post/34143/466008



volnet 2010-04-06 19:42 发表评论
]]>
关于U程同步的一些ȝQ用h?内核模式Q?/title><link>http://www.shnenglu.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.shnenglu.com/mymsdn/archive/2009/12/26/about-multithread-sync.html</guid><wfw:comment>http://www.shnenglu.com/mymsdn/comments/104111.html</wfw:comment><comments>http://www.shnenglu.com/mymsdn/archive/2009/12/26/about-multithread-sync.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/mymsdn/comments/commentRss/104111.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/mymsdn/services/trackbacks/104111.html</trackback:ping><description><![CDATA[<p><h3>自旋锁同?/h3><ol><li>一般是Z内核态下各个zN函数之间做同步作用的?/li><li>原理是(单CPUQ将IRQL从Y件中断提升到g中断。PASSIVE_LEVEL->DISPATCH_LEVEL。因为在DISPATCH_LEVEL中是不会出现U程切换的(只有高别能打断低别,而低U别不能打断高别)?/li><li>因ؓ分页内存导致如果线E切换的时候会引v分页数据交换Q数据交换是通过引发|障来实现的,而页故障是不允许出现在DISPATCH_LEVEL中的Q否则将引vpȝ崩溃QPASSIVE_LEVEL则允许)。驱动程序的StartIO例程、DPC例程、中断服务例E都q行在DISPATCH_LEVEL或者更高的IRQL。因此这些例E不能用分内存,否则导致系l崩溃?/li><li>自旋锁在不同IRP之间同步的时候,则需要放在DeviceExtension中传递?/li></ol></p><p><h3>互锁</h3><ol><li>cM于number++; //汇编后将不止一条语句,非原子操作number--; //同上因ؓ语句会变成多句,在线E切换的时候,两个U程下的该例E将会交l在一h行,D错误。可以:<div id="n39bdvn" class="gc-code">先加?br />number++;<br />解锁<br />再加?br />number--;<br />解锁<br /></div>来实C句话的同步(按指定顺序执行,而不受到U程切换的媄响)加锁解锁可以使用自旋?/li><li>在系l中提供了Interlocked***/ExInterlocked***实现</li></ol></p><p><h3>信号灯同?/h3><ol><li>U程1关闭信号灯,以至于用Wait****的时候,当前U程处于暂停状态?/li><li>U程2的作用就是在执行l束后,点亮信号灯(增加计数器)。当U程切换回来的时候,U程1因数器不是0而信号灯处于激zȝ态,从而l执行线E??/li></ol></p><p><h3>事g的同?/h3>Q不能递归获取互斥体)<ol><li>ȝE在辅助U程上设|了事gQ如果不使用Wait**{待事gq回Q则ȝE可能直接执行完毕了Q而导致辅助线E还在执行?/li><li>使用Wait****可以使主U程{待事g执行完成?/li></ol></p><p><h3>互斥体同?/h3>Q允讔R归获取互斥体(得到互斥体的U程q可以再ơ获得这个互斥体Q或者说互斥体对于已l获得互斥体的线E不产生“互斥”关系Q)<ol><li>创徏一个互斥体对象Q将互斥体对象传递给多个U程?/li><li>在每个线E操作的步骤中,调用Wait*****Q如果互斥体处于Ȁz(内部l护一个计数器Q,则l执行后l代码,q在调用l束后恢复互斥体Release****Q这样当别的U程试图使用互斥体后面的代码的时候,因ؓ互斥体状态未Ȁz,则无法l执行代码?/li></ol></p><p><h3>快速互斥体同步</h3><ol><li>与互斥体同步cMQ唯一区别是不允许递归获取互斥?/li></ol></p><img src ="http://www.shnenglu.com/mymsdn/aggbug/104111.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/mymsdn/" target="_blank">volnet</a> 2009-12-26 05:53 <a href="http://www.shnenglu.com/mymsdn/archive/2009/12/26/about-multithread-sync.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何~译TrueCrypt源码http://www.shnenglu.com/mymsdn/archive/2009/12/23/how-to-complie-TrueCrypt-63a.htmlvolnetvolnetWed, 23 Dec 2009 15:47:00 GMThttp://www.shnenglu.com/mymsdn/archive/2009/12/23/how-to-complie-TrueCrypt-63a.htmlhttp://www.shnenglu.com/mymsdn/comments/103863.htmlhttp://www.shnenglu.com/mymsdn/archive/2009/12/23/how-to-complie-TrueCrypt-63a.html#Feedback7http://www.shnenglu.com/mymsdn/comments/commentRss/103863.htmlhttp://www.shnenglu.com/mymsdn/services/trackbacks/103863.html相关配置
       
  • Intel x86 Core 2 Duo
  •    
  • Windows 7 Ultimate x86 version
  •    
  • Windows Driver Develop Kit 7600.16385.0
  •    
  • TrueCrypt 6.3a Source.zip
  •    
  • Microsoft Visual Studio 2008 SP1 (VC++ 2008)
  •    
  • Microsoft Visual Studio VC++ 1.52
  •    
  • NASM version 2.07 compiled on Jul 19 2009
  •    
  • gzip 1.2.4 Win32 (02 Dec 97)
  •    
  • ......

配置TrueCrypt

  1. 下蝲MSVC++ 1.52Q安装在C盘下QC:\MSVC
  2. 下蝲NASMQ也安装在C盘下QC:\NASM
    http://www.nasm.us/pub/nasm/releasebuilds/2.07/win32/
  3. 下蝲GZIPQ也安装在C盘下QC:\gzip
  4. 下蝲q安装WINDDKQhttp://www.microsoft.com/whdc/DevTools/WDK/WDKpkg.mspx
    我将它们安装在D盘,路径QD:\WinDDK
  5. 讄pȝ变量Q(WIN7Q控刉板\所有控刉杉K\pȝ\高pȝ讄\环境变量Q:pȝ变量中,新增Q?br />变量1QMSVC16_ROOT    |C:\MSVC
    变量2QWINDDK_ROOT    |D:\WinDDK\7600.16385.0
    其中7600.16385.0为WinDDK的第二目录名,同时也是版本P7600是Windows7的发行版本号?br />双击变量QPATHQ在其值的末尾补上QC:\NASM;C:\gzip
    目的是ؓ了让我们可以直接在命令行实用nasm以及gzip作ؓ命o行?br />
  6. 下蝲PKCS11Q三个文Ӟ右键另存为即可?br />http://svn.openvpn.net/projects/openvpn/test/time/openvpn/pkcs11-headers/
    三个文Ӟpkcs11.h、pkcs11f.h、pkcs11t.hQ拷贝到源码下的Common文g夹下Q我的源码放在D盘根目录Q三个源码我放?#8220;D:\TrueCrypt\Common”文g夹中?br />
  7. ~译Q会发现有两个错误?br />CKR_NEW_PIN_MODE和CKR_NEXT_OTP未定义,补充定义一下即可?br />在d:\TrueCrypt\Common\pkcs11t.h文g里(h据您自己的\径进行复Ӟ
    q里它们设|ؓQ?br />
    #define CKR_NEW_PIN_MODE      0x000001B0
    #define CKR_NEXT_OTP          0x000001B1
    我的Ҏ是找到实用它的语句附q的同类语句Q找到相同的定义Q在其下Ҏ加?br />比如Q?br />
                TC_TOKEN_ERR (CKR_MUTEX_NOT_LOCKED)
                TC_TOKEN_ERR (CKR_NEW_PIN_MODE)
                TC_TOKEN_ERR (CKR_NEXT_OTP)
    q三句话攑֜一P后两句有问题Q但W一句正常,则查找CKR_MUTEX_NOT_LOCKED的存放位|,在其下方d如上两句Q其中定义的值参?br />http://www.cryptsoft.com/pkcs11doc/STANDARD/include/v220/otp-pkcs11.hQ这里的值只不过是一U错误码Q只要它不重复,可以了?br />
  8. 再编译,可能会遇C些警告:
    1. nasm.exe正在停止Q而因为没有正执行,又导?#8220;fatal error LNK1136: invalid or corrupt file”错误?br />遇到q个可能是因Z的nasm正在试图~译ase_amdX64.asm文gQ而nasm?4位的asm~译对你可能意义不大Qv码对我而言是这LQ于是,我就它转成~译x86体系架构的,也许是因为参数配|的问题Q你可以试别的ҎQ如果有更好的话Q请告诉我?br />q里我搜索:x64、asm{关键字Q修改d:\TrueCrypt\Crypto\Makefile.inc文gZ面这样即可:
      ?    !if "$(TC_ARCH)" == "x86"
      ?    TC_OBJ_FORMAT = win32
      ?    !else
      ?    #TC_OBJ_FORMAT = win64
      ?    #edit by gocool, if the x64 system need the nasm.exe use the x64 format parameters for executing.
      ?    #abort the x64 system here for building.
      ?    #2009/12/23
      ?    TC_OBJ_FORMAT = win32
      ?    TC_ARCH = x86
      ?0    !endif
      ?1   
      ?2    "$(OBJ_PATH)\$(O)\Aes_$(TC_ARCH).obj": Aes_$(TC_ARCH).asm
      ?3        nasm.exe -Xvc -f $(TC_OBJ_FORMAT) -Ox -D DLL_EXPORT -o "$@" -l "$(OBJ_PATH)\$(O)\Aes_$(TC_ARCH).lst" Aes_$(TC_ARCH).asm
      其中Qؓ了减变化量Q也利于以后恢复Q第4-7行ؓ注释Q第8?行我非x86的情况也定义成x86的情况,q样无论如何下面W?3行的语句都将执行以x86体系为结构的讄Q而这L讄通常是正的?br />
    2. fatal error LNK1000: Internal error during IncrBuildImage
      据说是Microsoft Visual Studio 2008的一个BUG。http://blog.csdn.net/just_one_two/archive/2009/10/05/4634391.aspx
      听说有两U方法,一U是Ҏ1Q需要下载补丁,我没有尝试。第二种通过修改配置的方法我成功了,步骤如下Q?br />ҎQ项?>属?>链接?>常规   下面?#8220;启用增量链接”Q将“?/INCREMENTAL)”改ؓ“?/INCREMENTAL:NO)”?br />不过q又引入了另外一个警告:3>FormatCom.obj : warning LNK4075: 忽略“/EDITANDCONTINUE”(׃“/INCREMENTAL:NO”规范)
      选择目,属?>配置属?>C/C++Q修?#8220;调试信息格式”?#8220;E序数据?/Zi)”卛_?br />
    3. 警告Q未扑ֈ下列环境变量
      4>目 : warning PRJ0018 : 未找C列环境变?
      4>$(PKCS11_INC)
      ׃目属性里讄有附加包含目?#8220;$(PKCS11_INC)”Q因此编译的时候会在系l变量里LPKCS11_INC目Q如果找不到Q则l出警告Q因此,我们需要手动补充这个项。方法同步骤5Q增加一个变量ؓPKCS11_INCQ|D:\TrueCrypt\CommonQ其中,值就是之前我们拷贝三个文Ӟpkcs11.h、pkcs11f.h、pkcs11t.hQ的目录?br />
    4. 如果不出意外的话Q你可能q会得到一个用了PKEY_AppUserModel_ID未定义的声明W的错误。这个是用于标识用户态应用程序的唯一标识。你可以在Setup.h文g中定义:
      /*---region add by gc---*/
      #include "wtypes.h"
          const PROPERTYKEY PKEY_AppUserModel_ID = {
              {
                  (unsigned long)2009,/*unsigned long  Data1;*/
                  (unsigned short)12,/*unsigned short Data2;*/
                  (unsigned short)23,/*unsigned short Data3;*/
                  0x44,0x55,0x55,0x55,0x55,0x55,0x55,0x55
              },/*GUID fmtid;*/
              (DWORD)PID_FIRST_USABLE /*DWORD pid;*/
          };
      /*---endregion---*/
      其中Q这个结构体是由GUID和PID共同l成的?br />

下蝲链接

  • TrueCrypt下蝲Qhttp://www.sfr-fresh.com/windows/misc/TrueCrypt-6.2a-Source.zip:a/Boot/Windows/Makefile
  • WinDDK下蝲Qhttp://www.microsoft.com/whdc/DevTools/WDK/WDKpkg.mspx
  • PKCS11下蝲Qhttp://svn.openvpn.net/projects/openvpn/test/time/openvpn/pkcs11-headers/
  • GZip下蝲Qhttp://www.gzip.org/ 或?http://www.gzip.org/gz124src.zip
  • Nasm下蝲Qhttp://www.nasm.us/pub/nasm/releasebuilds/2.07/win32/
  • MSVC1.52下蝲Qhttp://download.csdn.net/source/620960 Q?5.02MBQ(g|上很多人都在找1.52Q最后一个可以编?6bitE序的VC~译器)Q但官方|站上没有公开下蝲的链接,实在非常郁闷Q我从MSDN订阅下蝲Q收费的噢,杯具Q则?7.6MBQ,如果大家实在找不C载或?5.02MB的不可用Q可以联pL?/li>

    参考链?/h3>
    • http://blog.csdn.net/skyremember/archive/2009/09/17/4562090.aspx
    • http://blog.sina.com.cn/s/blog_4758691d0100d8mc.html
    • http://lll332.blog.163.com/blog/static/1553692220093404635752/
    • http://msdn.microsoft.com/en-us/library/aa373931%28VS.85%29.aspx
    • http://hi.baidu.com/hhacker/blog/item/2fc5b3fb0b24132a4f4aea1d.html
    • http://blog.csdn.net/just_one_two/archive/2009/10/05/4634391.aspx
    • http://blog.csdn.net/liufei_learning/archive/2009/12/21/5047632.aspx
    • http://msdn.microsoft.com/zh-cn/library/958x11bc%28VS.80%29.aspx
    • http://bbs.xiakexing.com/cgi-bin/topic.cgi?forum=22&topic=498


    volnet 2009-12-23 23:47 发表评论
    ]]>最大公U数问题http://www.shnenglu.com/mymsdn/archive/2009/03/04/75564.htmlvolnetvolnetWed, 04 Mar 2009 15:52:00 GMThttp://www.shnenglu.com/mymsdn/archive/2009/03/04/75564.htmlhttp://www.shnenglu.com/mymsdn/comments/75564.htmlhttp://www.shnenglu.com/mymsdn/archive/2009/03/04/75564.html#Feedback0http://www.shnenglu.com/mymsdn/comments/commentRss/75564.htmlhttp://www.shnenglu.com/mymsdn/services/trackbacks/75564.htmlimage

    image

    image

    image

    image 

    image

    image

    image

    image

    以上内容摘自《编E之》P150-154?/p>

    Z方便使用Q下面是可拷贝的代码Q?/p>

    Math.h

    #pragma once
    
    class Math
    {
    public:
        Math(void);
        ~Math(void);
    
    public :
        //~程之美P150-154
    
        //求最大公U数Q欧几里德——辗转相除法
        static int Gcd1(int x, int y);
    
        //求最大公U数Q欧几里德——辗转相除法Q变相将除法变成了减法)
        static int Gcd2(int x, int y);
    
        static int Gcd3(int x, int y);
    
        inline static bool IsEven(int x);
    
        inline static int Absolute(int x);
    };
    

    Math.cpp

    #include "Math.h"
    
    Math::Math(void)
    {
    }
    
    Math::~Math(void)
    {
    }
    
    int Math::Gcd1(int x, int y)
    {
        //y, x%y序不能错;
        return y ? Gcd1(y, x % y) : x;
    }
    
    int Math::Gcd2(int x, int y)
    {
        //与Gcd1相同的方式,但由于x%y计算速度较x-y要慢Q但效果相同Q所以换用x - y
        // 但用减法和除法不同的是,比如和,%20=10Q?20=70Q也是-4×=10
        // 也就是说q代ơ数较Gcd1而言通常是增加了?
        return y ? Gcd1(y, x - y) : x;
    }
    
    int Math::Gcd3(int x, int y)
    {
        if(x < y)
            return Gcd3(y, x);
        if(y == 0)
            return x;
        else
        {
            if(IsEven(x))
            {
                if(IsEven(y))
                    return (Gcd3(x >> 1, y >> 1) << 1);
                else
                    return Gcd3(x >> 1, y);
            }
            else
            {
                if(IsEven(y))
                    return Gcd3(x, y >> 1);
                else
                    return Gcd3(y, x - y);
            }
        }
    }
    
    bool Math::IsEven(int x)
    {
        return !(bool)x & 0x0001;
    }
    
    int Math::Absolute(int x)
    {
        return x < 0 ? -x : x;
    }

    Main.cpp

    #include <stdafx.h>
    #include <iostream>
    #include "Math.h"
    
    using namespace std;
    int _tmain(const int & arg)
    {
        cout<<"Math::Gcd1(42,30) = "<<Math::Gcd1(42,30)<<endl;
        cout<<"Math::Gcd1(30,42) = "<<Math::Gcd1(30,42)<<endl;
        cout<<"Math::Gcd1(50,50) = "<<Math::Gcd1(50,50)<<endl;
        cout<<"Math::Gcd1(0,0) = "<<Math::Gcd1(0,0)<<endl;
        cout<<"Math::Gcd1(-42,-30) = "<<Math::Gcd1(-42,-30)<<endl;
        cout<<"Math::Gcd1(-42,30) = "<<Math::Gcd1(-42,30)<<endl;
    
        cout<<"------------------------------"<<endl;
    
        cout<<"Math::Gcd2(42,30) = "<<Math::Gcd2(42,30)<<endl;
        cout<<"Math::Gcd2(30,42) = "<<Math::Gcd2(30,42)<<endl;
        cout<<"Math::Gcd2(50,50) = "<<Math::Gcd2(50,50)<<endl;
        cout<<"Math::Gcd2(0,0) = "<<Math::Gcd2(0,0)<<endl;
        cout<<"Math::Gcd2(-42,-30) = "<<Math::Gcd2(-42,-30)<<endl;
        cout<<"Math::Gcd2(-42,30) = "<<Math::Gcd2(-42,30)<<endl;
    
        cout<<"------------------------------"<<endl;
    
        cout<<"Math::Gcd3(42,30) = "<<Math::Gcd3(42,30)<<endl;
        cout<<"Math::Gcd3(30,42) = "<<Math::Gcd3(30,42)<<endl;
        cout<<"Math::Gcd3(50,50) = "<<Math::Gcd3(50,50)<<endl;
        cout<<"Math::Gcd3(0,0) = "<<Math::Gcd3(0,0)<<endl;
        cout<<"Math::Gcd3(-42,-30) = "<<Math::Gcd3(-42,-30)<<endl;
        cout<<"Math::Gcd3(-42,30) = "<<Math::Gcd3(-42,30)<<endl;
    
        return 0;
    }

    不过有一点值得一提,是所谓性能最好效率最高的Gcd3不支持负敎ͼ也就是最后两行测试代码无法通过。但是限于对负数的最大公U数q没有定义,也就是说即便上面的Gcd1和Gcd2好像出了负敎ͼ但它们的l果没有意义?/p>

    volnet 2009-03-04 23:52 发表评论
    ]]>
    Emacs 快速指?/title><link>http://www.shnenglu.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.shnenglu.com/mymsdn/archive/2008/10/01/emacs-tutorial-chinese-gb.html</guid><wfw:comment>http://www.shnenglu.com/mymsdn/comments/57235.html</wfw:comment><comments>http://www.shnenglu.com/mymsdn/archive/2008/10/01/emacs-tutorial-chinese-gb.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.shnenglu.com/mymsdn/comments/commentRss/57235.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/mymsdn/services/trackbacks/57235.html</trackback:ping><description><![CDATA[     摘要: 初入EmacsQ还是要入乡随俗的,ZUnix的程序还是在键盘命o上下了很大的功夫Q但q样的通用性也得到了极大的提高。下面是拯Emacs的快速指南的中译版本Q只是记录一下,没有别的意思。呵呉|供一下Windowsq_Emacs的安装方式:1、下载EmacsZWindows的安装包Q我是从ftp://ftp.keystealth.org/pub/gnu/gnu/emacs/windows/ema...  <a href='http://www.shnenglu.com/mymsdn/archive/2008/10/01/emacs-tutorial-chinese-gb.html'>阅读全文</a><img src ="http://www.shnenglu.com/mymsdn/aggbug/57235.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/mymsdn/" target="_blank">volnet</a> 2008-10-01 03:09 <a href="http://www.shnenglu.com/mymsdn/archive/2008/10/01/emacs-tutorial-chinese-gb.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[知识库][转蝲]十年MFCl历认识的Microsoft技?ZT)http://www.shnenglu.com/mymsdn/archive/2006/12/04/15988.htmlvolnetvolnetMon, 04 Dec 2006 14:05:00 GMThttp://www.shnenglu.com/mymsdn/archive/2006/12/04/15988.htmlhttp://www.shnenglu.com/mymsdn/comments/15988.htmlhttp://www.shnenglu.com/mymsdn/archive/2006/12/04/15988.html#Feedback0http://www.shnenglu.com/mymsdn/comments/commentRss/15988.htmlhttp://www.shnenglu.com/mymsdn/services/trackbacks/15988.html 知识?KnowledgeLibrary)  

    固定链接Q?a href="/mymsdn/category/3173.html">http://www.shnenglu.com/mymsdn/category/3173.html


    文章标题Q十qMFCl历认识的Microsoft技?ZT)
    关键?/strong>QMFC、Microsoft、技术对比、技术发?br />转蝲?/strong>Q?a >http://blog.vckbase.com/hangwire/archive/2005/05/26/5806.html
    作?/strong>Qhangwire
    发表旉Q?005-05-26 09:13


    一、初识MFC

      我最初知道MFC大概是在1993q_那个时候Visual C++q没面世Q当时Microsoft的C++~译器还很弱Q官方的名字是Microsoft C/C++ 7.0QMFC的版本是1.0Q几乎没有引起什么反响,那个时期最好的C++开发环境是Borland C++ 3.1Q其实,大概?992q?1月䆾Q一个偶然的ZQ我领略到Borland公司的厉宻IC得在什么地方,我看C个绝妙的集成开发环境,即Turbo C++ 3.0 for WindowsQ这是我记忆中第一个真正的Windows环境下的C++集成开发环境,那种Ȁ动的感觉至今仍记忆犹斎ͼ不客气的_当时臛_在C++斚wQMicrosoft与Borland不是一个水q的QBorland明显的要高于Microsoft QBorland的品在技术上l我留下深刻的印象。那个时候Microsoft最好的开发^台是Visual Basic 3.0Q而Borland的Delphi正处于开发阶D(Delphi 的代码名U是Q?VB Killer"Q……,惌vq些十几q前的往事,我不感慨万千?/p>

      十几q来Q我用过许多开发环境,关于Visual BasicQ我用过最早的DOS版本QWindows版的Visual Basic我基本上全都用过Q至今我q记得每个版本的VB安装盘磁盘的盘数。同P我用q各个版本的DelphiQ特别是Delphi 2.0Q给我留下极好的印象。Delphi提供真正~译的可视化开发环境,那个时候(1994q左叻IQDelphi可以开发带有GUI的动态链接库Q你可以惌Q在Microsoft Access 2.0的应用程序中可以加蝲一个Delphi Formq进行程序交互,那种感觉真是极了?/p>

      Borland C++是我心中无法Ҏ的遗憾,从Turbo C到C++ BuilderQ我深刻的体验到Borland的辉煌和无奈QDelphi从VB Killer走到为VB护航Q你可以惌Delphi一步到位的ActiveX 控g开发技术有多牛Q早期的VB有多土,早期的VB不能开发动态链接库Q因此无法开发ActiveX 控gQ想h真o人嘘唏不ԌQBorland C++的命q也是不。Borland C++ 3.1的辉煌永q不再了Q十几年的开发工作中Q我在C++上投入了大量的精力,Borland C++曄l我带来无数的激动,然而这个经典的名字却在与Microsoft的竞争中渐渐的流逝了……?/p>

      MFC4.0的出玎ͼ使得Z感觉Microsoft在C++斚w赶上来了Q这一版的MFC是Win95推出后出现在Visual C++ 4中(Microsoft没有VC 3QVC4以前的版本是2.2?.1?.0?.51?.5?.0Q。也许是对Borland C++的潜意识的失望,我不知不觉的接受了MFCQVC 4.2推出Ӟ我通过正常渠道购买了这个编译器的企业版?/p>

    二、关于Microsoft

      关于MicrosoftQ有无数的h要对q个名字叙说感觉Q这个o厌的名字Q不知道是喜Ƣ还是憎Ӟ你是E序员,你的心思可能就要因Microsoft的存在而动Q即使你用LinuxQ你可能也是因ؓMicrosoft技术因素。多年来,q个名字每天都出现在你、我、他的面前,因ؓ你不得不面对Windows的存在,可是你憎恨这个名字吗Q你讨厌q个名字吗?我不知道是否已经对这个名字麻木了?/p>

      1998q我个h订了Microsoft MSDN Universal 版,我开始比较全面接触这个公司的开发技术,你可以想象,1998q当你面对上癑ּ技术光盘的时候,你就知道什么叫?厚度"Q当我们有时说出"赶上"?"辑ֈ"Microsoft某些产品的水q的时候,可能我们~Z对这个公?厚度"的真实了解。进入MSDNQ我感觉Microsoft直不是一?公司"Q而是Q或者正在Ş成)一?C会"?/p>

      当时著名的技术网?a >http://www.codeguru.com全部的技术资料是可下载的Q那个时?a >http://www.codeguru.com提供整个|站内容下蝲服务Q大U?M左右Q,大名鼎鼎?a 的景象?可能很多人想q,如果Borland不存在,对Microsoft不是更有力吗Q其实Microsoft可能_N中国历Ԍ读过《三国》、十分了解战国时期的中国Q其实Borland形式上的存在Q对Microsoft是十分有利的Q至Ş式上q有竞争ҎQ而事实上Borland已经受控于MicrosoftQMicrosoft是Borland的大股东Q。你可以看到一些微妙的现象QBorland为Microsoft提供了大量的人才Q其中包括Delphi总设计师以及Borland C++~译器的核心成员Q同时也为Microsoft .NET提供强有力的护航服务Q看看C# Builder、Delphi .NETQ?998qMicrosoft 的COM技术基本已l成熟,q个技术人感到震|当时Microsoft的对手们提出"OpenDoc"用于Ҏ"COM"Q你看看"OpenDoc"阵营的几个成员:IBM、Apple、Borland、NovellQ你会感到这个阵营十分豪华、强大。但l果却差Zh意,"OpenDoc"无疾而终Q?COM"依然生机勃勃?/p>

      有h?COM"没落了,那么太不了解Microsoft了。在?OpenDoc"的竞争中Q?COM"是个d的胜利者,在与"Java"的竞争中Q?COM"成功的进化了Q在q个q程中Microsoft体现了强大的吸收能力、以及无法想象的韧劲?NET只不q是COM?别名"而已。对于一个经验丰富的C++E序员而言Q?NET是COM的进化,而Microsoft内部.NET是"COM 3.0"QOLE2是COM 2.0Q,?CLR"是一个不择不扣的COM对象。曾l有人问我,既然牛顿时代奠定了基础Q想惌名的牛顿-莱布D公式Q,几百q后的今天,数学q研I?微积?吗?回答当然是依然在研究Q?微积?早期是针对函数的Q现?微积?是针?ŞQManifoldQ、纤l丛QFiber BundleQ?的,概念深奥了,可是基本思想不变Q只?微积?的思想得到合理的g拓与q化Q你了解Microsoft吗?Microsoft Research有一批超一的数学家在为Microsoft工作Q其中一些是斐尔兹奖的得主,Microsoft正在实现如同"微积?q化?微分Ş"一样将"COM"q化?.NET"。从U学概念角度上分析COM与JavaQ可能COM更全面、精,从实现的成熟度上Java可能更成熟,可是你看刎ͼMicrosoft正在不紧不慢的追赶。Microsoft令h联想h国时期的强秦?/p>

      战国时期的秦国,采取"q交q攻""抚弱掠强"{措施傲视六国,今天的Microsoft也是q样QVB1.0ӞMicrosoft推出"VBX"控g技术,众多的小公司得以生存QMicrosoft自己不开?VBX"lgQ同?VBX"q化?OCX"ӞMicrosoftq不十分强大Q可是这U试探得C多小公司的响应?997qMicrosoft Office 97?998qMicrosoft推出Visual Studio 6.0Q给众多中、小公司提供了生存、发展的ZQ例如Microsoft Office 97中集成了Visual Basic for Application 5.0Q这Ҏ术得几癑֮软g开发商与Microsoft{v了VBA技术许可协议,即AutoDeskq样的公叔R与Microsoft{v了这个协议,q个协议使得每个集成VBA的品的l个用户许可为Microsoft?0$的许可费Q如果你了解VSIPQVisual Studio Integration ProtocolQ协议,以及有多公司签订了VSIP协议Q你q正感觉到Microsoft的可怕;Microsoft Office 97、Visual Studio 6.0的用L面十分漂亮,Z么Microsoft自己的开发工具不提供cM的Y件组Ӟ你看C多第三方的Microsoft盟友UL推出自己的界面库以模仿MicrosoftQ他们不会反对MicrosoftQ因Z们已lŞ成了使得Microsoft以及q些公司得以生存的生态圈?/p>

      Microsoft的技术储备有多少QMicrosoft之外的h很难说清楚,Microsoft中国公司也未必了解多,1999qWTLcd刚刚出现的时候,Z希望WTL能得到官方的支持Q或授权l一个Microsoft之外的一个公司(你能惌出Borland C++ 5.0内置的ActiveX开发机制是ZMicrosoft ATLcd吗?Q,直到今天QWTL依然如故Q我们完全相信,如果Microsoft强力推广WTLQWTL完全可以行Q可是Microsoft不缺cM的技术,cM的类库还有BCL(Base Control LibraryQ一个用于开发轻量ActiveX控g的类?QMicrosoftq有一个基于ATL的类库,q个cd用于开发ActiveX DesignerQActiveX Designer是绝大多数程序员不了解的一cd象,如果你熟悉Office开发,你知道Office VBA 中有一cd象,即Form2Q此外VB6.0 中的报表设计器(以及著名的Active ReporterQ,都属于此cd象,用这个类库,你可以ؓVB6.0以及集成VBA的系l提供定制化的可视化设计机制{等Q如今ActiveX Designer已经演化为集成于Visual Studio .NET中的设计器?/p>

    三、向Microsoft学习

      无论从什么角度评价MicrosoftQ我觉得Microsoft是值得我们学习的,如果说生zdq个时代有Microsoft存在是一场灾难,你就应该痛恨q个家伙Q但你首先要向这个家伙学习!我无意ؓMicrosoft歌功颂dQ我只是惌出十几年我对Microsoft技术的感受?/p>

      Microsoft在研I式的开发中受益极大Q如果你有兴,你可以访?a U学无国?。技术是否有国界Q不知道是否有定论?Q想想DVD{技术专利给国内业界带来的灾难,不知道应不应该痛定思痛Q在Microsoft校园招聘现场的气氛中Q我g明白了ؓ什么国?原创技?得可怜。我读过几本Microsoft亚洲研究院的高手写的书,明显可以看出QBill gate 是他们的_领袖以及他们对Microsoft的虔诚,国内的研I机构应当研I一下Microsoft的用Z道,Microsoft好像是三国里的h物,不知是刘备还是曹操,或者二者的混合物。我l常路过西格玛大厦,W一ơ西格玛大厦q入真有"朝圣"的感觉,也与Microsoft中国的几个层ơ的人打q交道,各中滋味实在一a隑ְ?/p>

      在Office大战中,国软g的确在一些方面与Microsoftq行较量Q其实给人的感觉很勉强,界面上的似是而非Q或用户习惯斚w的接qƈ不能解决Ҏ的问题,一个好的Y件开发h员必L一个Y件用的高手Q很难想象一个Y件操作水q_拙劣的开发h员能开发出高水q的软gQ我最早用的软g之一是Microsoft WordQ当时的版本?.0Q大概是1992q的事情Q给我留下深d象的是集成于Word中的Word BasicQ后来,我接触到Excel 3.0Q不出所料,Excel中集成的是Excel BasicQ后来用的Access中自然内|Access Basic 1.0Q在q些软g集成捆绑成Office之前Q我感觉这些品的构思十分了不vQ很hMicrosoft的风|因ؓ你知道,即是一个DOSQMicrosoft都要提供一个内|的QBasic或GW Basic。虽然关于Microsoft的品评论很多,作ؓ一个技术h员,我认为Microsoft的品构思绝ҎW一的Q从1994q早期的Officepd?997qŞ成的Office 4.2Q我认ؓQ技术构思上均领先于我国2002q以后的Office产品Q你听说q如下说法吗Q?Dos 作ؓ操作pȝ的时代,Windows是应用YӞWindows是操作系l时QOffice成ؓDos时代的WindowsQ那么如果按此规律,Office会不会替代Windows而成为操作系l?"Q现在在开发领域Visual Studio( .NET)正在成ؓ另一个OfficeQ你注意C吗?控制Visual Studio( .NET)集成开发环境的仍然是一个Basic语言引擎QVisual Basic .NETQ?

      与许多公怸同的是,在技术体pMQMicrosoft几乎所有的产品是息息相关的QWindows、Office、Visual Studio .NET虽然各不相同Q但公共的核心即Ş成,我们已经看到Q核心组件方面,Office与Visual Studio .NET日渐于一_例如Microsoft正在Office 2003的核心组件VBA 6.X逐步用新的Visual Studio Tools for Office替代Q而我们依然在一些似是而非的现象上与Microsoft的品比较差距,国家采购或政府采购支持的公司Q不去钻研核心技术,只是急功q利的采用短期行为急于与Microsoft怺Q不知是否有蚍蜉撼树的感觉,个h的体验是Q先学习MicrosoftQ踏t实实的学,了解MicrosoftQ深入的了解Q然后再喊口受?/p>

    四、ؓ什么用MFCQ?/p>

      l过若干q的竞争QBorland 的OWL几乎消失了,q个OWL是个非常漂亮的C++cdQ在Borland C++ 3.1风光无限的年代,OWL真正的做C独领风骚。然而,Borland C++ 4.0错过了进?2位程序的最x机,BC 4.0推出后不久,q来了Win95QBorland仓促上阵Q以一个小?Pack"使得BC4可以~译ZWin4的程序,当时的Visual C++?.0版,支持Window16的版本ؓVisual C++1.51Q有意思的是Borland可以用同一个编译器同时支持Win16、Win32Q而Microsoft却不得不为Win16、Win32提供不同的编译器。然而,非正式版本的Visual C++ 2.1与Visual C++ 2.2却悄悄地支持了Win95的最新特征,即Win95新提供的一l公共控Ӟ在我的印象中QBorland对Win95新特征的支持不利使得MFC与OWL的距L大的~短了。稍后到来的Borland C++ 4.5没有改变q个状况Q尽Borland C++ 5.0同时支持OWL与MFCQ可是|象已l显ԌBorland C++非常遗憾的只走到?.5版。C++ Builder虽然形式上引入了Delphi的VCL库,可是许多C++E序员ƈ不买账,因ؓ许多以C++Z的h更喜Ƣ以~辑的模式进行编码。Visual C++ 4.0的出玎ͼ在C++q个战场上,Borland开始落败了?/p>

      MFC发展C天,已经十多q了Q尽褒贬不一Q但可以肯定Q十几年的技术积累已l奠定了MFC的生存基Q即使Microsoft的长角发布,MFC也不能推出Windows的舞収ͼ事实上,长角QLonghornQ之后的Visual Studio .NET仍将MFC作ؓ一个重要的l成部分Q在今年的Visual Studio .NET 2005中,MFC在C++中的位置依然如故。MFC的未来,应该不必担心Q只要你深入考察.NETcdQ你会发玎ͼMFC的许多思想机制正悄然进?NETQ与此同ӞMicrosoft的第三方盟友十多q来已ؓMFC开发了大量的扩展库Q如果Microsoft是船Q第三方盟友是载舟之水。许多h认ؓMFC不发展了Q其实是一U错觉,Visual C++ 6的界面十分经典,特别是其中的Docking控制条机Ӟ其实Visual C++ 6的IDE完全是MFC写的Q可是MFCcd中控制条相关的类功能很弱Qؓ什么?你会看到许多与Microsoft友好的公司,他们很快的在MFC基础上实CVisual C++ 6 的Docking机制Q这是Microsoft的高明之处,Microsoft很会l盟友提供机会,其一贯的做法是在自q商品化品中预先提供一些有的特征Q得其他一些公司进行模仿以带动用户体。Borland不具备这L储备。MFCW三方市场的J荣Q得益于Microsoft的策略与明智。MFC可否跨^収ͼ理论上完全可以,Microsoft不做Q也是策略,但是有许多重要的产品Microsoft却默许MFCUL到其他^収ͼ事实上,Microsoft的合作伙伴之一Mainsoft公司QWindows源码是从这家公司流qQ,几年来就是负责移植MFCE序UL到UINIX、Linux、AIX{操作系l之上?/p>

      新版的Visual C++中MFC已经支持.NET开发了QMFC与ATL的协作更好了。根据我的经验,MFC、ATL?NET库三者完全可以融合在一L合应用到实际的开发工作中去,如果你是MFC行家Q我希望ATL?NET库能成ؓ你的忠实的左x。那么有没有同时支持MFC、ATL?NET库的E序Q当然有QVisual Studio .NET IDE是Q而且Visual Studio .NET IDEq支持用ATL?NET库扩展的AddinQ如果你希望用MFC理ATL?NET库,Ll支持我Q?/p>

    五、认识Application对象

      如果你熟悉Microsoft OfficeQ你应该q一步的剖析q个大型软gQMicrosoft Office中几乎每个程序都是可二次开发的Q这一点得益于Microsoft Office内置的二ơ开发机Ӟ一个是ZCOM机制的VBA模型Q另一个是Z.NET框架的托模型:Visual Studio Tools for Office。作Z名程序员Q你应当在技术角度解析Office的技术结构。Microsoft的大多数软g的对象结构可以通过Visual Studio提供的工具OLE/COM Object Viewer考察其类型库得到Q通过引用cd库,你甚臛_以得到描q对象信息的C++头文件。这样做真是好处多多。一个典型的Office通常都有一个Application对象Q或其他一个与之相当的对象Q,q个对象相当于Y件枢U,在这里,我们不讨论OfficeQ借此话题说说Application对象。大多数支持扩展QAddin、PluginQ的软g都存在类似的构造。通常Q一个系l得Application对象或者是一个COM对象Q或者是一?NET对象Q如果你的系l存在这cd象,你的pȝ基本具备支持Addin、Plugin的机制了。一个理想的做法是在一个MFCpȝ中,内置一个ATL对象?NET对象Q稍后我们给出方案如何做到这一炏V设计Application对象的关键是如何规划q个对象的属性、方法、事件。如果你希望pȝ具备良好的扩展性,Application对象是十分关键的Q这也是构架艺术的体现。所谓Addin(Plugin)Q是pȝq行时根据需要加载的对象库,Addin(Plugin)之所以可以扩展系l,关键的因素就是系l加载Addin(Plugin)ӞApplication对象传递给Addin(Plugin)库,设想一下,如果Application恰到好处的触发了pȝ事gQ而Addin(Plugin)库如愿的解释了事Ӟ一个Addin(Plugin)库的d不就OK了吗Q因此Application对象是系l设计的关键?/p>

      如果你精通ATL对象Q在你的MFCpȝ中添加一个ATL对象Q这个Q务可以用VC Wizard完成。你已经接受了一个事实,是MFCE序中存在一个CXXXApp对象QCWinApp的派生类Q,现在你要做的是增加一个对应得ATL对象。这个对象可以在CXXXApp::InitInstance()中创建,如果ATL对象的类是CXXXAppObjectQ徏议你在CXXXApp对象对象中增加一个成员变量,例如QCComObject<CXXXAppObject>* m_pAppObjQ然后可以入下初始化m_pAppObjQ?/p>

    m_pAppObj = new CComObject<CXXXAppObject>Q?/p>

      注意E序l束时在CXXXApp::ExitInstance()中释放m_pAppObjQ语句如下:

    delete m_pAppObjQ?/p>

      你可以将pȝ得关键属性设|成CXXXAppObject的属性,例如pȝ得标题、是否ؓ多文等{。系l希望外部调用的功能可以实现为CXXXAppObject的方法,q一点取决于你的需要。系l需要外部扩展的功能Q表CؓCXXXAppObject的事Ӟ关键是在恰当的位|触发事件以及提供的事g参数。例如,你可以在CXXXApp::InitInstance()触发应用E序开始的事gOnStartUpQPlugin捕获事g后,可以q行特定的初始化Qn份确认、初始信息查询等{)Q你可以在CXXXApp::ExitInstance()触发应用E序l束事gQPlugin捕获事g后,处理用户需要的pȝ退出工作。所有的设计取决于具体设计?/p>

      如何加蝲PluginQ是一个有的问题Q如果Plugin实现Z个COM范畴QCategoryQ,可以q用COM技术枚举这个CategoryQ可以将Plugin安装C个特定目录,也可以通过注册表。Plugin的实现可以用COM技术、也可以?NET框架。适当的机会我会提供例子…?/p>

    六、后?/p>

      一时心血来潮Q就写了q篇文章Q很难说是有心,q是无意。几天前我在新浪|上看应氏杯围棋册Q我觉得该赢了吧Q作Z个围迷Q我们等了十几年Q等C属于国h的应氏杯。记??q前在还在大学工作的时候,有一ơ,一位同事兴致冲冲的走道我面前对我说Q?嗨,昨天马XX赢了李昌镐!"Q当时我在系办公室正在看报纸Q那位仁兄见我头都没抬,非常不满的抢下报U,Ҏ吼道Q?喂!马XX赢了李昌镐!Q你听到没有Q!Q?Q我对他_"你大惊小怪个啥?Q马XX输了李昌镐多盘Q你知道吗?"Q马XX几乎一直在输给李昌镐,Z已经不奇怪了Q偶赢一ơ,国h把他捧得北都找不到了,李昌镐弱?7的时候就傲视q个世界了,可至今面孔不变,几天前的农心杯,中日联军5个hQ被他打个落花流_李昌镐是公认的世界第一Q以至于有的高手知道下一个对手如果是他,׃去订回程机票。这ơ应氏杯Q国人竟然感谢崔哲瀚,何也Q因个弱?9的小子,挡住了他的大哥李昌镐才得应氏杯有了悬念。当国h媒体在说韩国仅李昌镐一人厉害的时候,不知道是Z居心q是自欺ZhQ李昌镐q方30Q不知道要力压中、日多少q_面对q个名字Q真有点L了,q个太极虎!

      软g界又来了我们一向不齿的印度虎,2001q我们的软g出口额仅是印度的四十分之一Q我们震惊了Q怎么可能呢?q个四十分之一水分很大Q很可能更可怜!当时我在大连参加一个关?大连软g出口国内W一"的官方会议,那位大h在会上说Q?据说Q我们大qY件出口国内排名第一Q市有关领导希望今天的会议给个第一的数字依据,希望你们把数据报上来Q去q的数据也可xQ注意,我们要的只是数据Q你们仔l体会,我们Ҏ数据Q有奖励Q机会难得呀Q?……。某一天,几个朋友在我家看央视的对话节目,对话一方ؓ国内的Y件大鳄们Q用友、阿派{公司的老MQ,另一方ؓ印度软g的一个代表团。当问及中、印软g差距的时候,我们的刘老总(代表阿尔z)不以为然的说Q据他的看法Q我们已l快赶上Q印度)了,……,a下之意颇有印度的水^不过如此的感觉,印度方的话我至今记忆ҎQ?是否赶上Q国际市的算Q在中国看来Q印度程序员的个性不I技术也不怎么P其实是个错觉Q印度Y仉先注重个性,许多重要的美国商品化软g都是在印度本土开发的…?Q我们的舆论L印度程序员的水qxq的q_xQ可是差距日渐拉开Q……,围棋、球(不好意思谈Q谈不出口!Q、YӞ我们被近ML锁了,乐坏了记者们、给媒体带来了生机…?

      日本江户时代的围,如果一个h要想世袭一个称P例如Q本因坊Q,他必L胜所有的师兄弟,然后Q住q师父家的内室,你知道以后的事情吗?以后Q这个棋手,得为师父一家做饭、带孩子、搞卫生……,其余的门人则一心一意的下棋Q这L人、方式,造就了一代一代的本因坊,他们的棋谱大多数都流芌今,q就是早期日本围的悟道模式。Y件d有多语句?我最早接触的计算Y件教材是一本英文版的(影印的D版)Q不同于我们Q那本书的作者构造了"X-语言"Q他们不讲什么C、Pascal、BasicQ一旦缺了什么机Ӟq"X-语言"d些成分。什么C、Pascal、BasicQ你感觉差不多,但现在却分出了等U!我们N语言的能力弱得很Q可是我们在语言的细微之处却很讲IӞ不知道对不对Q许多程序员也许是出于虚荣而用C++Q事实上Q地球h到知道,做数据库QDelphi、VBq比C++胜QQ铺天盖地的C++的书Q写的东西几乎雷同,因ؓQ有用的或者作者不写、或者作者不懂。有时我在想Q如果国内没有内需Q会怎样Q也许Y件内需的存在,造就了中国Y件的特色Q我认ؓ国内业界q没有充分利用中国Y件内需的存在,也许中国软g内需的存在是软g落后的硬伤?/p>

      我记得一部电影《神辫》,那个英雄的大辫子被洋人炸掉了Q最l他成了枪手,战胜zh用大刀、秘c是不行的,用洋的东西战胜洋的技术才是正道。我觉得Q一个好的程序员必须了解软g的历Ԍ学习历史Q你知道你ؓ什么弱Q别人是如何强大的。我们正在另一个战Z抗美Q可W的是我们却要赶印度!Q,无论Microsoft、Borland如何争斗Q无Z们谁l治谁,他们不媄响美国的强大Q朋友们Q学习MicrosoftQ开发出让国人感到牛的YӞ



    volnet 2006-12-04 22:05 发表评论
    ]]> þݺҹҹ2O2O| 鶹wwwþ| þˬˬav| þۺ97ɫֱ| þҹɫƷAV| Ӱһþҹײ | ޾ҹþþþþ| ޹˾Ʒþþùһ | Ʒþþ21p| 97þ㽶߿ۿ| RE99þþƷ66| þþþ99ƷƬŷ| þerƷѹۿ8| þþþƷһ | þ99Ʒ99þ6| ݺɫþþۺƵպ| þõӰ| Ļþۺ| þۺϸۺϾþ| 91Ʒ91Ⱦþþþø | ɫվwwwþþ| 99þþƷž޾Ʒ| þþƷձҰ| ޾Ʒҹvaþ| һþaþþƷۺҹҹ | ŷ츾þþþþò| ƷëٸAVѾþ| þþƷư| ҹҹݺݾþAV| þ91Ʒ91| þùƷ| պAVëƬƷþþ| þþþ| ŷ鶹þþþþ| ˾Ʒþ޸岻| Ʒþþþþù91| þۺɫһ| ޺ݺۺϾþ| þ91ᆱƷ2020| ޾Ʒtvþþþ| ޹˾þһWWW|