Alex's profile大漠孤烟直,长河落日圆。PhotosBlogListsMore Tools Help

Blog


    December 20

    C#设计模式(10)-Adapter Pattern

    Referrence:

    http://www.cnblogs.com/zhenyulu/articles/39386.html

    结构模式(Structural Pattern)描述如何将类或者对象结合在一起形成更大的结构。结构模式描述两种不同的东西:类与类的实例。根据这一点,结构模式可以分为类的结构模式和对象的结构模式。

    后续内容将包括以下结构模式:

    • 适配器模式(Adapter):Match interfaces of different classes
    • 合成模式(Composite):A tree structure of simple and composite objects
    • 装饰模式(Decorator):Add responsibilities to objects dynamically
    • 代理模式(Proxy):An object representing another object
    • 享元模式(Flyweight):A fine-grained instance used for efficient sharing
    • 门面模式(Facade):A single class that represents an entire subsystem
    • 桥梁模式(Bridge):Separates an object interface from its implementation


    一、 适配器(Adapter)模式

    适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本接口不匹配而无法在一起工作的两个类能够在一起工作。

    名称由来

    这很像变压器(Adapter),变压器把一种电压变换成另一种电压。美国的生活用电电压是110V,而中国的电压是220V。如果要在中国使用美国电器,就必须有一个能把220V电压转换成110V电压的变压器。这个变压器就是一个Adapter。

    Adapter模式也很像货物的包装过程:被包装的货物的真实样子被包装所掩盖和改变,因此有人把这种模式叫做包装(Wrapper)模式。事实上,大家经常写很多这样的Wrapper类,把已有的一些类包装起来,使之有能满足需要的接口。

    适配器模式的两种形式

    适配器模式有类的适配器模式和对象的适配器模式两种。我们将分别讨论这两种Adapter模式。


    二、 类的Adapter模式的结构:

    由图中可以看出,Adaptee类没有Request方法,而客户期待这个方法。为了使客户能够使用Adaptee类,提供一个中间环节,即类Adapter类,Adapter类实现了Target接口,并继承自Adaptee,Adapter类的Request方法重新封装了Adaptee的SpecificRequest方法,实现了适配的目的。

    因为Adapter与Adaptee是继承的关系,所以这决定了这个适配器模式是类的。

    该适配器模式所涉及的角色包括:

    目标(Target)角色:这是客户所期待的接口。因为C#不支持多继承,所以Target必须是接口,不可以是类。
    源(Adaptee)角色:需要适配的类。
    适配器(Adapter)角色:把源接口转换成目标接口。这一角色必须是类。


    三、 类的Adapter模式示意性实现:

    下面的程序给出了一个类的Adapter模式的示意性的实现:

    //  Class Adapter pattern -- Structural example 
    using System;

    // "ITarget"
    interface ITarget
    {
    // Methods
    void Request();
    }

    // "Adaptee"
    class Adaptee
    {
    // Methods
    public void SpecificRequest()
    {
        Console.WriteLine("Called SpecificRequest()" );
      }
    }

    // "Adapter"
    class Adapter : Adaptee, ITarget
    {
    // Implements ITarget interface
    public void Request()
    {
    // Possibly do some data manipulation
    // and then call SpecificRequest
    this.SpecificRequest();
      }
    }

    /**//// <summary>
    /// Client test
    /// </summary>
    public class Client
    {
    public static void Main(string[] args)
    {
    // Create adapter and place a request
        ITarget t = new Adapter();
        t.Request();
      }
    }


    四、 对象的Adapter模式的结构:

    从图中可以看出:客户端需要调用Request方法,而Adaptee没有该方法,为了使客户端能够使用Adaptee类,需要提供一个包装(Wrapper)类Adapter。这个包装类包装了一个Adaptee的实例,从而将客户端与Adaptee衔接起来。由于Adapter与Adaptee是委派关系,这决定了这个适配器模式是对象的。

    该适配器模式所涉及的角色包括:

    目标(Target)角色:这是客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口。
    源(Adaptee)角色:需要适配的类。
    适配器(Adapter)角色:通过在内部包装(Wrap)一个Adaptee对象,把源接口转换成目标接口。


    五、 对象的Adapter模式示意性实现:

    下面的程序给出了一个类的Adapter模式的示意性的实现:

    // Adapter pattern -- Structural example 
    using System;

    // "Target"
    class Target
    {
    // Methods
    virtual public void Request()
    {
    // Normal implementation goes here
      }
    }

    // "Adapter"
    class Adapter : Target
    {
    // Fields
    private Adaptee adaptee = new Adaptee();

    // Methods
    override public void Request()
    {
    // Possibly do some data manipulation
    // and then call SpecificRequest
        adaptee.SpecificRequest();
      }
    }

    // "Adaptee"
    class Adaptee
    {
    // Methods
    public void SpecificRequest()
    {
        Console.WriteLine("Called SpecificRequest()" );
      }
    }

    /**//// <summary>
    /// Client test
    /// </summary>
    public class Client
    {
    public static void Main(string[] args)
    {
    // Create adapter and place a request
        Target t = new Adapter();
        t.Request();
      }
    }


    六、 在什么情况下使用适配器模式

    在以下各种情况下使用适配器模式:

    1、 系统需要使用现有的类,而此类的接口不符合系统的需要。
    2、 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。这些源类不一定有很复杂的接口。
    3、 (对对象适配器而言)在设计里,需要改变多个已有子类的接口,如果使用类的适配器模式,就要针对每一个子类做一个适配器,而这不太实际。


    七、 一个实际应用Adapter模式的例子

    下面的程序演示了Class Adapter与Object Adapter的应用。

    // Example of implementing the Adapter pattern
    using System;

    // Target
    public interface  ICar
    {
    void  Drive();
    }

    // Direct use without Adapter
    public class  CToyota : ICar
    {
    public void  Drive()
    {
        Console.WriteLine("Vroom Vroom, we're off in our Toyota");
      }
    }

    // Adaptee
    public class  CCessna
    {
    public void  Fly()
    {
        Console.WriteLine("Static runup OK, we're off in our C172");
      }
    }

    // Class Adapter
    public class  CDrivableCessna : CCessna, ICar
    {
    public void  Drive()  {  base.Fly();  }
    }

    // Object Adapter
    public class  CDrivableCessna2 : ICar
    {
    private CCessna  m_oContained;

    public CDrivableCessna2()
    {
        m_oContained = new CCessna();
      }

    public void  Drive()  {  m_oContained.Fly();  }
    }

    // Client
    public class  Client
    {
    public static void  Main(string[] args)
    {
        ICar  oCar = new CToyota();

        Console.Write("Class Adapter: Driving an Automobile");
        oCar.Drive();
        oCar = new CDrivableCessna();
        Console.Write("Driving a Cessna");
        oCar.Drive();
        oCar = new CDrivableCessna2();
        Console.Write(" Object Adapter: Driving a Cessna");
        oCar.Drive();
      }
    }


    八、 关于Adapter模式的讨论

    Adapter模式在实现时有以下这些值得注意的地方:

    1、 目标接口可以省略,模式发生退化。但这种做法看似平庸而并不平庸,它可以使Adaptee不必实现不需要的方法(可以参考Default Adapter模式)。其表现形式就是父类实现缺省方法,而子类只需实现自己独特的方法。这有些像模板(Template)模式。
    2、 适配器类可以是抽象类。
    3、 带参数的适配器模式。使用这种办法,适配器类可以根据参数返还一个合适的实例给客户端。


    参考文献:
    阎宏,《Java与模式》,电子工业出版社
    [美]James W. Cooper,《C#设计模式》,电子工业出版社
    [美]Alan Shalloway  James R. Trott,《Design Patterns Explained》,中国电力出版社
    [美]Robert C. Martin,《敏捷软件开发-原则、模式与实践》,清华大学出版社
    [美]Don Box, Chris Sells,《.NET本质论 第1卷:公共语言运行库》,中国电力出版社

    Bridge Pattern

    Referrence:

    http://www.cnblogs.com/zhenyulu/articles/62720.html

    一、 桥梁(Bridge)模式

    桥梁模式是一个非常有用的模式,也是比较复杂的一个模式。熟悉这个模式对于理解面向对象的设计原则,包括"开-闭"原则(OCP)以及组合/聚合复用原则(CARP)都很有帮助。理解好这两个原则,有助于形成正确的设计思想和培养良好的设计风格。

    注:《Java与模式》一书认为Bridge模式不是一个使用频率很高的模式,我不太赞同,我认为Bridge模式中蕴涵了很多设计模式的关键思想在里面,所以我这里采纳了《Design Patterns Explained》一书的作者Alan Shalloway与James R. Trott的观点:The Bridge pattern is quite a bit more complex than the other patterns you just learned; it is also much more useful.

    桥梁模式的用意

    【GOF95】在提出桥梁模式的时候指出,桥梁模式的用意是"将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化"。这句话有三个关键词,也就是抽象化、实现化和脱耦。

    抽象化

    存在于多个实体中的共同的概念性联系,就是抽象化。作为一个过程,抽象化就是忽略一些信息,从而把不同的实体当做同样的实体对待【LISKOV94】。

    实现化

    抽象化给出的具体实现,就是实现化。

    脱耦

    所谓耦合,就是两个实体的行为的某种强关联。而将它们的强关联去掉,就是耦合的解脱,或称脱耦。在这里,脱耦是指将抽象化和实现化之间的耦合解脱开,或者说是将它们之间的强关联改换成弱关联。

    将两个角色之间的继承关系改为聚合关系,就是将它们之间的强关联改换成为弱关联。因此,桥梁模式中的所谓脱耦,就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以相对独立地变化。这就是桥梁模式的用意。


    二、 桥梁模式的结构

    桥梁模式【GOF95】是对象的结构模式,又称为柄体(Handle and Body)模式或接口(Interface)模式。

    下图所示就是一个实现了桥梁模式的示意性系统的结构图。

    可以看出,这个系统含有两个等级结构,也就是:

    • 由抽象化角色和修正抽象化角色组成的抽象化等级结构。
    • 由实现化角色和两个具体实现化角色所组成的实现化等级结构。

    桥梁模式所涉及的角色有:

    • 抽象化(Abstraction)角色:抽象化给出的定义,并保存一个对实现化对象的引用。
    • 修正抽象化(Refined Abstraction)角色:扩展抽象化角色,改变和修正父类对抽象化的定义。
    • 实现化(Implementor)角色:这个角色给出实现化角色的接口,但不给出具体的实现。必须指出的是,这个接口不一定和抽象化角色的接口定义相同,实际上,这两个接口可以非常不一样。实现化角色应当只给出底层操作,而抽象化角色应当只给出基于底层操作的更高一层的操作。
    • 具体实现化(Concrete Implementor)角色:这个角色给出实现化角色接口的具体实现。


    三、 桥梁模式的示意性源代码

    // Bridge pattern -- Structural example 
    using System;

    // "Abstraction"
    class Abstraction
    {
    // Fields
    protected Implementor implementor;

    // Properties
    public Implementor Implementor
    {
    set{ implementor = value; }
      }

    // Methods
    virtual public void Operation()
    {
        implementor.Operation();
      }
    }

    // "Implementor"
    abstract class Implementor
    {
    // Methods
    abstract public void Operation();
    }

    // "RefinedAbstraction"
    class RefinedAbstraction : Abstraction
    {
    // Methods
    override public void Operation()
    {
        implementor.Operation();
      }
    }

    // "ConcreteImplementorA"
    class ConcreteImplementorA : Implementor
    {
    // Methods
    override public void Operation()
    {
        Console.WriteLine("ConcreteImplementorA Operation");
      }
    }

    // "ConcreteImplementorB"
    class ConcreteImplementorB : Implementor
    {
    // Methods
    override public void Operation()
    {
        Console.WriteLine("ConcreteImplementorB Operation");
      }
    }

    /**//// <summary>
    /// Client test
    /// </summary>
    public class Client
    {
    public static void Main( string[] args )
    {
        Abstraction abstraction = new RefinedAbstraction();

    // Set implementation and call
        abstraction.Implementor = new ConcreteImplementorA();
        abstraction.Operation();

    // Change implemention and call
        abstraction.Implementor = new ConcreteImplementorB();
        abstraction.Operation();
      }
    }


    四、 调制解调器问题

    感觉《敏捷软件开发-原则、模式与实践》中关于Bridge模式的例子很好。(《Java与模式》一书33章的对变化的封装一节也写得很不错,推荐大家读一读。它深入的阐述了《Design Patterns Explained》一书中"1)Design to interfaces. 2)Favor composition over inheritance. 3)Find what varies and encapsulate it"的三个观点。)。

    如图所示,有大量的调制解调器客户程序在使用Modem接口。Modem接口被几个派生类HayesModem、USRoboticsModem和EarniesModem实现。它很好地遵循了OCP、LSP和DIP。当增加新种类的调制解调器时,调制解调器的客户程序不会受影响。

    假定这种情形持续了几年,并有许多调制解调器的客户程序都在使用着Modem接口。现出现了一种不拨号的调制解调器,被称为专用调制解调器。它们位于一条专用连接的两端。有几个新应用程序使用这些专用调制解调器,它们无需拨号。我们称这些使用者为DedUser。但是,客户希望当前所有的调制解调器客户程序都可以使用这些专用调制解调器。他们不希望去更改许许多多的调制解调器客户应用程序,所以完全可以让这些调制解调器客户程序去拨一些假(dummy)电话号码。

    如果能选择的话,我们会把系统的设计更改为下图所示的那样。

    我们把拨号和通信功能分离为两个不同的接口。原来的调制解调器实现这两个接口,而调制解调器客户程序使用这两个接口。DedUser只使用Modem接口,而DedicateModem只实现Modem接口。但这样做会要求我们更改所有的调制解调器客户程序--这是客户不允许的。

    一个可能的解决方案是让DedicatedModem从Modem派生并且把dial方法和hangup方法实现为空,就像下面这样:

    几个月后,已经有了大量的DedUser,此时客户提出了一个新的更改。为了能拨国际电话号码、信用卡电话、PIN标识电话等等,必修对现有dial中使用char[10]存储号码改为能够拨打任意长度的电话号码。

    显然,所有的调制解调器客户程序都必须更改。客户同意了对调制解调器客户程序的更改,因为他们别无选择。糟糕的是,现在必须要去告诉DedUser的编写者,他们必须要更改他们的代码!你可以想象他们听到这个会有多高兴。本来他们是不用调用dial的。

    这就是许多项目都会具有的那种有害的混乱依赖关系。系统某一部分中的一个杂凑体(kludge)创建了一个有害的依赖关系,最终导致系统中完全无关的部分出现问题。

    如果使用ADAPTER模式解决最初的问题的话,就可以避免这个严重问题。如图:

    请注意,杂凑体仍然存在。适配器仍然要模拟连接状态。然而,所有的依赖关系都是从适配器发起的。杂凑体和系统隔离,藏身于几乎无人知晓的适配器中。

    BRIDGE模式

    看待这个问题,还有另外一个方式。现在,出现了另外一种切分Modem层次结构的方式。如下图:

    这不是一个理想的结构。每当增加一款新硬件时,就必须创建两个新类--一个针对专用的情况,一个针对拨号的情况。每当增加一种新连接类型时,就必须创建3个新类,分别对应3款不同的硬件。如果这两个自由度根本就是不稳定的,那么不用多久,就会出现大量的派生类。

    在类型层次结构具有多个自由度的情况中,BRIDGE模式通常是有用的。我们可以把这些层次结构分开并通过桥把它们结合到一起,而不是把它们合并起来。如图:

    我们把调制解调器类层次结构分成两个层次结构。一个表示连接方法,另一个表示硬件。

    这个结构虽然复杂,但是很有趣。它的创建不会影响到调制解调器的使用者,并且还完全分离了连接策略和硬件实现。ModemConnectController的每个派生类代表了一个新的连接策略。在这个策略的实现中可以使用sendlmp、receivelmp、diallmp和hanglmp。新imp方法的增加不会影响到使用者。可以使用ISP来给连接控制类增加新的接口。这种做法可以创建出一条迁移路径,调制解调器的客户程序可以沿着这条路径慢慢地得到一个比dial和hangup层次更高的API。


    五、 另外一个实际应用Bridge模式的例子

    该例子演示了业务对象(BusinessObject)通过Bridge模式与数据对象(DataObject)解耦。数据对象的实现可以在不改变客户端代码的情况下动态进行更换。

    // Bridge pattern -- Real World example
    using System;
    using System.Collections;

    // "Abstraction"
    class BusinessObject
    {
    // Fields
    private DataObject dataObject;
    protected string group;

    // Constructors
    public BusinessObject( string group )
    {
    this.group = group;
      }

    // Properties
    public DataObject DataObject
    {
    set{ dataObject = value; }
    get{ return dataObject; }
      }

    // Methods
    virtual public void Next()
    { dataObject.NextRecord(); }

    virtual public void Prior()
    { dataObject.PriorRecord(); }

    virtual public void New( string name )
    { dataObject.NewRecord( name ); }

    virtual public void Delete( string name )
    { dataObject.DeleteRecord( name ); }

    virtual public void Show()
    { dataObject.ShowRecord(); }

    virtual public void ShowAll()
    {
        Console.WriteLine( "Customer Group: {0}", group );
        dataObject.ShowAllRecords();
      }
    }

    // "RefinedAbstraction"
    class CustomersBusinessObject : BusinessObject
    {
    // Constructors
    public CustomersBusinessObject( string group )
        : base( group ){}

    // Methods
    override public void ShowAll()
    {
    // Add separator lines
        Console.WriteLine();
        Console.WriteLine( "------------------------" );
    base.ShowAll();
        Console.WriteLine( "------------------------" );
      }
    }

    // "Implementor"
    abstract class DataObject
    {
    // Methods
    abstract public void NextRecord();
    abstract public void PriorRecord();
    abstract public void NewRecord( string name );
    abstract public void DeleteRecord( string name );
    abstract public void ShowRecord();
    abstract public void ShowAllRecords();
    }

    // "ConcreteImplementor"
    class CustomersDataObject : DataObject
    {
    // Fields
    private ArrayList customers = new ArrayList();
    private int current = 0;

    // Constructors
    public CustomersDataObject()
    {
    // Loaded from a database
        customers.Add( "Jim Jones" );
        customers.Add( "Samual Jackson" );
        customers.Add( "Allen Good" );
        customers.Add( "Ann Stills" );
        customers.Add( "Lisa Giolani" );
      }

    // Methods
    public override void NextRecord()
    {
    if( current <= customers.Count - 1 )
          current++;
      }

    public override void PriorRecord()
    {
    if( current > 0 )
          current--;
      }

    public override void NewRecord( string name )
    {
        customers.Add( name );
      }

    public override void DeleteRecord( string name )
    {
        customers.Remove( name );
      }

    public override void ShowRecord()
    {
        Console.WriteLine( customers[ current ] );
      }

    public override void ShowAllRecords()
    {
    foreach( string name in customers )
          Console.WriteLine( " " + name );
      }
    }

    /**//// <summary>
    /// Client test
    /// </summary>
    public class BusinessApp
    {
    public static void Main( string[] args )
    {
    // Create RefinedAbstraction
        CustomersBusinessObject customers =
    new CustomersBusinessObject(" Chicago ");

    // Set ConcreteImplementor
        customers.DataObject = new CustomersDataObject();

    // Exercise the bridge
        customers.Show();
        customers.Next();
        customers.Show();
        customers.Next();
        customers.Show();
        customers.New( "Henry Velasquez" );

        customers.ShowAll();
      }
    }

    六、 在什么情况下应当使用桥梁模式

    根据上面的分析,在以下的情况下应当使用桥梁模式:

    • 如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的联系。
    • 设计要求实现化角色的任何改变不应当影响客户端,或者说实现化角色的改变对客户端是完全透明的。
    • 一个构件有多于一个的抽象化角色和实现化角色,系统需要它们之间进行动态耦合。
    • 虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。

    参考文献:
    阎宏,《Java与模式》,电子工业出版社
    [美]James W. Cooper,《C#设计模式》,电子工业出版社
    [美]Alan Shalloway  James R. Trott,《Design Patterns Explained》,中国电力出版社
    [美]Robert C. Martin,《敏捷软件开发-原则、模式与实践》,清华大学出版社
    [美]Don Box, Chris Sells,《.NET本质论 第1卷:公共语言运行库》,中国电力出版社

    December 13

    C++中几个比较不常用的关键字

    作者:vcforever  出处:CSDN

    mutable关键字

      关键字mutable是C++中一个不常用的关键字,他只能用于类的非静态和非常量数据成员
      我们知道一个对象的状态由该对象的非静态数据成员决定,所以随着数据成员的改变,
      对像的状态也会随之发生变化!

      如果一个类的成员函数被声明为const类型,表示该函数不会改变对象的状态,也就是
      该函数不会修改类的非静态数据成员.但是有些时候需要在该类函数中对类的数据成员
      进行赋值.这个时候就需要用到mutable关键字了

      例如:

      class Demo
      {
      public:
      Demo(){}
      ~Demo(){}
      public:
      bool getFlag() const
      {
      m_nAccess++;
      return m_bFlag;
      }
      private:
      int  m_nAccess;
      bool m_bFlag;
      };

      int main()
      {
      return 0;
      }

      编译上面的代码会出现 error C2166: l-value specifies const object的错误
      说明在const类型的函数中改变了类的非静态数据成员.

      这个时候需要使用mutable来修饰一下要在const成员函数中改变的非静态数据成员
      m_nAccess,代码如下:

      class Demo
      {
      public:
      Demo(){}
      ~Demo(){}
      public:
      bool getFlag() const
      {
      m_nAccess++;
      return m_bFlag;
      }
      private:
      mutable int  m_nAccess;
      bool m_bFlag;
      };

      int main()
      {
      return 0;
      }

      这样再重新编译的时候就不会出现错误了!

    volatile关键字

      volatile是c/c++中一个鲜为人知的关键字,该关键字告诉编译器不要持有变量的临时拷贝,它可以适用于基础类型
      如:int,char,long......也适用于C的结构和C++的类。当对结构或者类对象使用volatile修饰的时候,结构或者
      类的所有成员都会被视为volatile.

      使用volatile并不会否定对CRITICAL_SECTION,Mutex,Event等同步对象的需要
      例如:
      int i;
      i = i + 3;
      无论如何,总是会有一小段时间,i会被放在一个寄存器中,因为算术运算只能在寄存器中进行。一般来说,volatitle
      关键字适用于行与行之间,而不是放在行内。

      我们先来实现一个简单的函数,来观察一下由编译器产生出来的汇编代码中的不足之处,并观察volatile关键字如何修正
      这个不足之处。在这个函数体内存在一个busy loop(所谓busy loop也叫做busy waits,是一种高度浪费CPU时间的循环方法)

      void getKey(char* pch)
      {
      while (*pch == 0)
      ;
      }

      当你在VC开发环境中将最优化选项都关闭之后,编译这个程序,将获得以下结果(汇编代码)

      ;       while (*pch == 0)
      $L27
      ; Load the address stored in pch
      mov eax, DWORD PTR _pch$[ebp]
      ; Load the character into the EAX register
      movsx eax, BYTE PTR [eax]
      ; Compare the value to zero
      test eax, eax
      ; If not zero, exit loop
      jne $L28
      ;
      jmp $L27
      $L28
      ;}

      这段没有优化的代码不断的载入适当的地址,载入地址中的内容,测试结果。效率相当的低,但是结果非常准确

      现在我们再来看看将编译器的所有最优化选项开关都打开以后,重新编译程序,生成的汇编代码,和上面的代码
      比较一下有什么不同

      ;{
      ; Load the address stored in pch
      mov eax, DWORD PTR _pch$[esp-4]
      ; Load the character into the AL register
      movsx al, BYTE PTR [eax]
      ; while (*pch == 0)
      ; Compare the value in the AL register to zero
      test al, al
      ; If still zero, try again
      je SHORT $L84
      ;
      ;}

     从代码的长度就可以看出来,比没有优化的情况要短的多。需要注意的是编译器把MOV指令放到了循环之外。这在单线程中是一个非常好的优化,但是,在多线程应用程序中,如果另一个线程改变了变量的值,则循环永远不会结束。被测试的值永远被放在寄存器中,所以该段代码在多线程的情况下,存在一个巨大的BUG。解决方法是重新
      写一次getKey函数,并把参数pch声明为volatile,代码如下:

      void getKey(volatile char* pch)
      {
      while (*pch == 0)
      ;
      }

      这次的修改对于非最优化的版本没有任何影响,下面请看最优化后的结果:

      ;{
      ; Load the address stored in pch
      mov eax, DWORD PTR _pch$[esp-4]
      ;       while (*pch == 0)
      $L84:
      ; Directly compare the value to zero
      cmp BYTE PTR [eax], 0
      ; If still zero, try again
      je SHORT $L84
      ;
      ;}

      这次的修改结果比较完美,地址不会改变,所以地址声明被移动到循环之外。地址内容是volatile,所以每次循环
      之中它不断的被重新检查。

      把一个const volatile变量作为参数传递给函数是合法的。如此的声明意味着函数不能改变变量的值,但是变量的
      值却可以被另一个线程在任何时间改变掉。

      explicit关键字

      我们在编写应用程序的时候explicit关键字基本上是很少使用,它的作用是"禁止单参数构造函数"被用于自动型别转换,其中比较典型的例子就是容器类型,在这种类型的构造函数中你可以将初始长度作为参数传递给构造函数.
      例如:
      你可以声明这样一个构造函数

      class Array
      {
      public:
      explicit Array(int size);
      ......
      };

      在这里explicit关键字起着至关重要的作用,如果没有这个关键字的话,这个构造函数有能力将int转换成Array.一旦这种情况发生,你可以给Array支派一个整数值而不会引起任何的问题,比如:

      Array arr;
      ...
      arr = 40;

      此时,C++的自动型别转换会把40转换成拥有40个元素的Array,并且指派给arr变量,这个结果根本就不是我们想要的结果.如果我们将构造函数声明为explicit,上面的赋值操作就会导致编译器报错,使我们可以及时发现错误.需要注意的是:explicit同样也能阻止"以赋值语法进行带有转型操作的初始化";

     例如:
      Array arr(40);//正确
      Array arr = 40;//错误

      看一下以下两种操作:
      X x;
      Y y(x);//显式类型转换
      另一种
      X x;
      Y y = x;//隐式类型转换

      这两种操作存在一个小小的差别,第一种方式式通过显式类型转换,根据型别x产生了型别Y的新对象;第二种方式通过隐式转换产生了一个型别Y的新对象.
      explicit关键字的应用主要就是上面所说的构造函数定义种,参考该关键字的应用可以看看STL源代码,其中大量使用了该关键字

      __based关键字

      该关键字主要用来解决一些和共享内存有关的问题,它允许指针被定义为从某一点开始算的32位偏移值,而不是内存种的绝对位置
      举个例子:

      typedef struct tagDEMOSTRUCT {
      int a;
      char sz[10];
      } DEMOSTRUCT, * PDEMOSTRUCT;

      HANDLE hFileMapping = CreateFileMapping(...);
      LPVOID lpShare = (LPDWORD)MapViewOfFile(...);

      DEMOSTRUCT __based(lpShare)* lpDemo;

      上面的例子声明了一个指针lpDemo,内部储存的是从lpShare开始的偏移值,也就是lpHead是以lpShare为基准的偏移值.上面的例子种的DEMOSTRUCT只是随便定义的一个结构,用来代表任意的结构.

      虽然__based指针使用起来非常容易,但是,你必须在效率上付出一定的代价.每当你用__based指针处理数据,CPU都必须为它加上基地址,才能指向真正的位置.

      在这里我只是介绍了几个并不时很常见的关键字的意义即用法,其他那些常见的关键字介绍他们的文章已经不少了在这里就不再一一介绍了.希望这些内容能对大家有一定的帮助!