深入剖析C++多态、VPTR指针、虚函数表

在讲多态之前,我们先来说说关于多态的一个基石------类型兼容性原则。

一、背景知识

  1.类型兼容性原则 

  类型兼容规则是指在需要基类对象的任何地方,都可以使用公有派生类的对象来替代。通过公有继承,派生类得到了基类中除构造函数、析构函数之外的所有成员。这样,公有派生类实际就具备了基类的所有功能,凡是基类能解决的问题,公有派生类都可以解决。类型兼容规则中所指的替代包括以下情况:

    子类对象可以当作父类对象使用

    子类对象可以直接赋值给父类对象

    子类对象可以直接初始化父类对象

    父类指针可以直接指向子类对象

    父类引用可以直接引用子类对象

  在替代之后,派生类对象就可以作为基类的对象使用,但是只能使用从基类继承的成员。类型兼容规则是多态性的重要基础之一。

#include <iostream>
using namespace std;

class Parent{
public:
    void printP(){
        cout<<"Parent"<<endl;
    }
    Parent(const Parent& obj){
        cout<<"拷贝构造函数"<<endl;
    }
private:
    int a;
};

class Son:public Parent
{
public:
    void printC(){
        cout<<"Son"<<endl;
    }
private:
    int b;
};

void howtoPrint(Parent *base){
    base->printP(); //只能执行父类的成员函数
}

void howtoPrint2(Parent &base){
    base.printP(); //只能执行父类的成员函数
}

void main(int argc, char const *argv[])
{
    Parent p1;
    p1.printP();

    Son s1;
    s1.printC();
    s1.printP();

    //类型兼容性原则
    //1-1.基类指针(引用)指向子类对象
    Parent *p = NULL;
    p1 = &s1;
    p1->printP();

    //2.指针做函数参数
    howtoPrint(&p1);//完全没问题
    howtoPrint(&s1);//完全没问题,兼容性原则

    //3.引用做函数参数
    howtoPrint2(p1);
    howtoPrint2(s1);

    //第二层含义:可以让子类对象初始化父类对象
    //子类就是一种特殊的父类
    Parent p2 = c1;//调用拷贝构造函数

    return 0;
}

  2.多态产生的背景探究

   我们先来看一个实例,然后慢慢引出为什么会有多态这一需求。

class Parent
{
public:
    Parent(int a){
        this->a = a;
    }
    printP(){
        cout<<"Parent"<<endll;
    }
private:
    int a;

};

class Son:public Parent{
public:
    Son(int b):Parent(10){
        this->b = b;
    }
    printP(){
        cout<<"Son"<<endll;
    }
private:
    int b;
};

void howtoPrint(Parent *base){
    base->printP();
}

void howtoPrint2(Parent &base){
    base.printP();
}

  上面定义了两个类,并且子类与父类都有一个同名函数printP(),现在,我们来看看测试案例:

    情形一:定义一个基类指针,让该指针分别指向基类和子类对象,然后调用printP():

void main(int argc, char const *argv[])
{
    Parent *p = NULL;
    Parent p1(20);
    Son s1(30);//

    p = &p1;//指针执行基类
    p1->printP();//

    p = &s1;//类型兼容性原则
    p->printP();
}

  对于第一个p1->printP(),我很快知道,执行的是父类的PrintP()函数;但是对于第二个,是执行子类还是父类?

  结果,我测试发现,也是执行的父类的printP()函数。

    情形二:定义基类别名和子类别名,然后使用别名来调用该函数:

void main(int argc, char const *argv[])
{

        Parent &base = p;//父类的别名
        base.printP();//执行父类

        Parent &base2 = s1;//别名
        base2.printP();//执行父类
}

    答案也是执行父类的printP()函数。

    情形三:定义一个函数,也就是上面写的howtoPrint(),函数参数为基类指针,然后定义一个指向基类指针,让该指针分别指向基类对象和子类对象:

void main(int argc, char const *argv[])
{
    Parent *p = NULL;
    Parent p1(20);
    Son s1(30);

    p = &p1;
    howtoPrint(&p1);
    p = &s1;
    howtoPrint(&s1);
}    

   答案都是执行父类的printP()函数。

    情形四:定义一个函数,也就是上面写的howtoPrint2(),函数参数为基类对象的引用,然后分别传入基类对象引用和子类对象引用:

void main(int argc, char const *argv[])
{
    Parent *p = NULL;
    Parent p1(20);
    Son s1(30);

    howtoPrint2(p1);
    howtoPrint2(s1);
}

   答案依然是调用基类的printP()函数。

  上面四种情形,不管我们怎么改变调用方式,始终都是调用的基类的函数,那么问题就来了,怎么样才能解决,传入子类对象调用子类函数,传入基类对象调用基类函数呢?

  因此,C++给我们提供了多态这一个解决方案。下面,我们来看看,多态是如何解决我们的需求的。

  2.多态

  要理解多态,首先要搞明白,什么是多态?

  多态:就是根据实际的对象类型决定函数调用语句的具体调用目标。

  总结一句话就是,同样的调用语句有多种不同的表现形态。

              

   在了解什么什么事多态以后,那么问题就来了,C++编译器是如何实现上述需求的呢?各位看官,接着往下看。 

二、虚函数

  1.定义:类成员函数前面添加virtual关键字以后,该函数被称为虚函数。

  2.声明

class Parent
{
public:
    Parent(int a){
        this->a = a;
    }
    virtual print(){
        cout<<"Parent"<<endll;
    }
private:
    int a;

};

  3.多态的实现

  上面我们说道,如果解决C++中,根据传入对象的不同来调用同样的函数呢?

  这就用到了C++提供的虚函数!关于虚函数实现机制,在下面我会慢慢剖析。

  下面,我们来看看多态的效果:

class Parent
{
public:
    Parent(int a){
        this->a = a;
    }
    virtual printP(){
        cout<<"Parent"<<endll;
    }
private:
    int a;

};

class Son:public Parent{
public:
    Son(int b):Parent(10){
        this->b = b;
    }
    printP(){ //子类的virtual写可不写,只需要父类写就可以了
        cout<<"Son"<<endll;
    }
private:
    int b;
};

  上面的继承关系很清晰,这里要注意一点:基类与子类的同名函数,要想实现多态,基类同名函数必须声明为virtual函数

  在定义了虚函数后,我在来看看上面出现的问题解决掉了没?我们写出测试案例:

  测试案例一:成功解决了问题

void main(int argc, char const *argv[])
{
    Parent *p = NULL;
    Parent p1(20);
    Son s1(30);

    p = &p1;
    p1->printP();//执行父类的打印函数

    p = &s1;
    p->printP();//执行子类的打印函数
}

  测试案例二:也成功解决问题

void main(int argc, char const *argv[])
{
    Parent p1(20);
    Son s1(30);

    Parent &base = p1;//父类的别名
    base.printP();//执行父类

    Parent &base2 = s1;//别名
    base2.printP();//执行子类
}

  测试案例三:

void howtoPrint(Parent *base){ //一个调用语句执行不同的函数
    base->printP();
}

void main(int argc, char const *argv[])
{
    Parent p1(20);
    Son s1(30);
    howtoPrint(&p1);//父类
    howtoPrint(&s1);//子类
}

  在测试案例三种,我们定义了一个函数howtoprint(),函数参数为基类指针,在主函数中,我们分别传入了子类对象和基类对象,输出结果显示:当传入基类对象,调用基类的printP()语句,当传入子类对象的时候,调用的是子类的printP()函数,结果也正好是我们想要的。也就是说,在执行howtoPrint()函数的时候,发生了多态。

  最后,我们在看看测试案例四:

void howtoPrint2(Parent &base){//一个调用语句执行不同的函数
    base.printP();
}

void main(int argc, char const *argv[])
{
    Parent *p = NULL;
    Parent p1(20);
    Son s1(30);

    howtoPrint2(p1);//父类
    howtoPrint2(s1);//子类

}

  正如我们想要的,一个调用语句执行不同的函数。

  哦,最后我成功的得到了我们想要的结果,但我们应该至此为止么?显然不能,多态的强大之处,我们还没真正的感受到,而且对于多态的实现机制,难道你就不想挖一挖吗?为什么会出现這种情况,难道你就没有疑惑吗?各位,别着急,继续往下看!

三、从一个案例中体会多态的强大之处

  场景:现在我们有三辆战机,两辆英雄战机和一辆敌机,其关系图如下:

                

  功能:当我们传入初级战机对象和敌机对象进行战斗时,判断输赢;当我们传入高级战机和敌机战斗,判断输赢;

  我们用两种方式来做做看,看看多态的强大之处:

class HeroFighter{
        public:
            virtual int power(){
                return 10;
            }
    };
    class AdvHeroFighter:public HeroFighter
    {
        public:
            int power(){
                return 20;
            }
    };
    class EnemyFighter
    {
    public:
        int attack(){
            return 15;
        }
    };

  上面定义了三个战机类,我们来看看测试代码:

  非多态使用方式:

void main(int argc, char const *argv[])
    {
        HeroFighter hf;
        AdvHeroFighter ahf;
        EnemyFighter ef;

        if (hf.power() > ef.attack())
        {
            cout<<"英雄胜"<<endl;
        }
        else{
            cout<<"英雄负"<<endl;
        }
        if (ahf.power() > ef.attack())
        {
            cout<<"英雄胜"<<endl;
        }
        else{
            cout<<"英雄负"<<endl;
        }
}

  上面这种方式,代码虽然结构很清晰,但是代码相似度很高,而且,一旦需求变化,又要新添许多逻辑判断。下面,我们看看多态是如何实现的。

void play(HeroFighter *hf,EnemyFighter *ef){
        if (hf->power() > ef->attack())//hf->power()调用会发生多态
            cout<<"英雄胜"<<endl;
        else
            cout<<"英雄负"<<endl;
    }

    void main(int argc, char const *argv[])
    {
        HeroFighter hf;
        AdvHeroFighter ahf;
        EnemyFighter ef;
        play(&hf,&ef);
        play(&ahf,&ef);
    }

  有没有发现,使用多态来实现,发现代码简洁多!我们在play()中定义了两个参数:一个是英雄基类指针,一个是敌机指针。当我们传入不同英雄战机对象时,在hf->power()这里会发生多态,根据对象的不同,调用不同类的同名函数。我们可以把play()函数看成一个框架,不管创建多少英雄战机,我们都能使用hf->power()来调用属于对象自己的函数。而且play()這个函数,我们并不需要做任何的改动!有没有发现多态很强大,其实在设计模式中,基本上都是依靠多态来的,可以说多态是设计模式的基石,这一点都不为过。

  有了上面的描述,我们很容易总结出多态实现的基础:

    1.要有继承

    2.要有虚函数重写

    3.父类指针(引用)指向子类对象

四、多态理论基础

  1.静态联编与动态联编

   联编:是指一个程序模块、代码之间相互关联的过程

    静态联编(static binding):是程序的匹配、连接在编译阶段实现,重载函数使用静态联编

    动态联编:是指程序联编推迟到运行时进行,又称为迟联编。switch和if语句是动态联编的例子

    关于静态联编和动态联编,我们下面拿上面的例子进行说明一下:

class HeroFighter{
        public:
            virtual int power(){
                return 10;
            }
    };

    class AdvHeroFighter:public HeroFighter
    {
        public:
            int power(){
                return 20;
            }
    };

    class EnemyFighter
    {
    public:
        int attack(){
            return 15;
        }
    };

三个战斗机类

void play(HeroFighter *hf,EnemyFighter *ef){ 

        if (hf->power() > ef->attack())//hf->power()调用会发生多态
            cout<<"英雄胜"<<endl;
        else
            cout<<"英雄负"<<endl;
    }

  我们知道在if判断里面会发生多态,如果power函数没有定义为virtual函数,那么这里称为的是静态联编,也就是说,hf->power()这里的调用关系,会在编译阶段根据函数参数会绑定到HeroFighter对象身上,这也就是为什么,不管以何种方式传入子类对象,都只能调用到基类的power()函数,因为这种调用关系,在编译的时候就已经确定了!

  如果power()函数被定义为虚函数,那么就称为动态联编

  即这种绑定关系并不会依靠函数参数来确定,而是在程序运行期间进行绑定。这也就是为什么当我们传入对象不同,会调用不同的函数。

  2.重载、重写、重定义 

       函数重载

    • 函数重载必须在同一个类中进行
    • 子类无法重载父类函数,父类同名函数将被名称覆盖
    • 重载是在编译器期间根据参数类型和个数决定函数调用(静态联编)

    函数重写

    • 函数重写必须发生在父类与子类之间
    • 父类与子类的函数原型完全一样
    • 使用virtual声明之后能够产生多态(如果不写virtual关键字,称为重定义)
    • 多态是在运行期间根据具体对象的类型来决定函数调用

          --------------非虚函数重写 --->重定义
      重写
          --------------虚函数重写----->重写(会发生多态)

class Parent
    {
    public:
        //以下三个函数在同一个类中表现为重载
        virtual void fun(){
            cout<<"func1()"<<endl;
        }
        virtual void fun(int i){
            cout<<"func2()"<<endl;
        }
        virtual void fun(int i,int j){
            cout<<"func3()"<<endl;
        }
        int abc(){
            cout<<"abc"<<endl;
        }
        virtual void fun(int i,int j,int k,int r){
            cout<<"fun(i,j,k,r)"
        }
    };
class Parent
    {
    public:
        int abc(){
            return 0;
        }
    };

    class Son:public Parent
    {
    public:
        int abc(){//非虚函数重写--->重定义
            cout<<"abc"<<endl;
            return 1
        }
};
class Parent
    {
    public:
        virtual void fun(int i,int j){
            cout<<"func3()"<<endl;
        }
};

class Son:public Parent
    {
    public:
        virtual void fun(int i,int j){//与父类相同,這是虚函数重写
            cout<<"fun(int i,int j)"<<endl;
        }
}

  注意:以下问题

class Parent
{
public:

    virtual void fun(){
        cout<<"func1()"<<endl;}
    virtual void fun(int i,int j,int k,int r){
        cout<<"fun(i,j,k,r)"}
};

class Son:public Parent
{
public:
    virtual void fun(int i,int j,int k){
        cout<<"func(int i,int j,int k)"<<endl;}
};

    void main(int argc, char const *argv[])
    {
        Son s;
        s.fun();//这会调用哪个?
        s.fun(1,2,3,5);//编译会咋样?
        return 0;
    }

  分析:上面执行编译都不能通过。我们慢慢分析,s.fun(),这里会报错,为什么呢?原因很简单,编译器首先会在子类的名称空间找,看自己有没有叫fun()的函数,如果有名字没fun()函数,就会执行自己的,至于参数个数问题,编译器可不管。它只管有没有叫這个名字,所以,在编译的时候,父类同名函数将被名称覆盖。如果想调用父类的函数,我们只有通过域作用符来调用s.parent::fun();

     同理s.fun(1,2,3,5);也不会执行父类的!

五、多态原理探究

  1.VPTR指针与虚函数表

  这里我们主要来探究一下,编译器在什么地方动了手脚,从而支持了多态?

  从一段代码来分析:

  下面代码,我把C++编译器可能动手脚地方标注出来,看看编译器到底是在什么时候就实现不同对象能调用同名函数绑定关系。

class Parent{
    public:
        Parent(int a=0){
            this->a = a;}
        virtual void print(){ //地方1
            cout<<"parent"<<endl;}
    private:
        int a;
};

class Son:public Parent{
    public:
       Son(int a=0,int b=0):Parent(a){
           this->b = b;}
       void print(){
           cout<<"Son"<<endl;}
    private:
        int b;
    };

  void play(Parent *p){ //地方2
        p->print();}

    void main(int argc, char const *argv[])
    {
        Parent p; //地方3
        Son s;
        play(&s)
        return 0;
    }

  在地方1处,我们知道,既然函数被声明了virtual,编译器会做特殊处理,会不会是这里确定了绑定关系呢?

  在地方2处,我们知道,当传来子类对象, 执行子类函数, 传来父类对象,执行父类对象,多态就是在这里发生的,那会不会在这里呢?

  但是,恰恰我们最容易忽视就是在地方3处,真正确定绑定关系的地方,就是创建对象的时候!!这时候C++编译器会偷偷的给对象添加一个vptr指针。

  只要我们在类中定义了virtual函数,那么我们在定义对象的时候,C++编译器会在对象中存储一个vptr指针,类中创建的虚函数的地址会存放在一个

虚函数表中,vptr指针就是指针這个表的首地址。

       

      

 

void play(Parent *p){
        p->print();
}

  在发生多态的地方,也就上面的,编译器根本不会去区分,传进来的是子类对象还是父类对象。而是关心print()是否为虚函数,如果是虚函数,就根据不同对象的vptr指针找属于自己的函数。而且父类对象和子类对象都会有vptr指针,传入对象不同,编译器会根据vptr指针,到属于自己虚函数表中找自己的函数。即:vptr--->虚函数表------>函数的入口地址,从而实现了迟绑定(在运行的时候,才会去判断)。

  如果不是虚函数,那么这种绑定关系在编译的时候就已经确定的,也就是静态联编!

  这里,关于虚函数表要说明两点:

  说明1:通过虚函数表指针VPTR调用重写函数是在程序运行时进行的,因此需要通过寻址操作才能确定真正应该调用的函数。而普通成员函数是在编译时就确定了调用的函数。在效率上,虚函数的效率要低很多。

  说明2:出于效率考虑,没有必要将所有成员函数都声明为虚函数

  说明3 :C++编译器,执行play函数,不需要区分是子类对象还是父类对象

  最后,我们来总结一下多态的实现原理:   

  • 当类中声明虚函数时,编译器会在类中生成一个虚函数表
  • 虚函数表是一个存储类成员函数指针的数据结构
  • 虚函数表是由编译器自动生成与维护的,virtual成员函数会被编译器放入虚函数表中

  2.如何证明VPTR指针的存在

class Parent1{
  public:
        Parent1(int a=0){
            this->a = a;}
        void print(){
            cout<<"parent"<<endl;}
    private:
        int a;};
class Parent2{
   public:
        Parent2(int a=0){
            this->a = a;}
        virtual void print(){
            cout<<"parent"<<endl;}
    private:
        int a;};

    void main(int argc, char const *argv[]){
        cout<<"Parent1"<<sizeof(Parent1)<<endl; //4
        cout<<"Parent2"<<sizeof(Parent2)<<endl; //8
        return 0;
    }

六、深入剖析VPTR指针

  问题:构造函数中能调用虚函数,实现多态么?

  等价于:对象中的vptr指针什么时候初始化?

  我们看一段代码:

class Parent{
public:
    Parent(int a=0){
            this->a = a;
            print();}
    virtual void print(){cout<<"Parent"<<endl;}
private:
    int a;};
class Son:public Parent{
    Son(int a=0,int b=0):Parent(a){
        this->b = b;
        print();}
    virtual void print(){cout<<"Son"<<endl;}
};
void main(int argc, char const *argv[]){
        Son s;
        return 0;
}

  当我们定义对象的时候,会执行构造函数,但是,在构造函数里面,我们调用了虚函数print(),那么这里的print()会执行哪个?会发生多态么??

  测试发现:两个类中构造函数中,都只会调用自己类中的print()函数。

  为什么会这样?为什么没有发生多态?

  

  

      

时间: 2024-10-07 12:22:09

深入剖析C++多态、VPTR指针、虚函数表的相关文章

C++多态篇3——虚函数表详解之多继承、虚函数表的打印

在上上一篇C++多态篇1一静态联编,动态联编.虚函数与虚函数表vtable中,我最后简单了剖析了一下虚函数表以及vptr. 而在上一篇文章C++多态篇2--虚函数表详解之从内存布局看函数重载,函数覆盖,函数隐藏中我详细介绍了虚函数的函数重载,函数覆盖以及函数隐藏的问题,其实在那一篇文章中,对单继承的虚函数已经做了十分详细的解答了,如果对前面有兴趣的人可以先看一下那篇文章. 在这一篇中,我会具体的分析一下在不同继承中(单继承,多继承)关于虚函数表在内存中的布局以及如何打印虚函数表.但是有关在虚继承

C++多态篇2——虚函数表详解之从内存布局看函数重载,函数覆盖,函数隐藏

上一篇C++多态篇1一静态联编,动态联编.虚函数与虚函数表vtable中,我在最后分析了虚函数与虚函数表的内存布局,在下一篇详细剖析虚函数及虚函数表的过程中,我发现有关函数重载,函数覆盖,函数重写和函数协变的知识也要理解清楚才能对虚函数表在内存中的布局,对派生类的对象模型以及对多态的实现有更深的理解. 所以这一篇我作为一篇过渡篇,也同时对我以前写过的一篇博文进行一个收尾.在C++继承详解之二--派生类成员函数详解(函数隐藏.构造函数与兼容覆盖规则)文章中,我对函数覆盖,重载,重写提了一下,但是没

C++ Primer 学习笔记_35_面向对象编程(6)--虚函数与多态(三):虚函数表指针(vptr)及虚基类表指针(bptr)、C++对象模型

C++ Primer 学习笔记_35_面向对象编程(6)--虚函数与多态(三):虚函数表指针(vptr)及虚基类表指针(bptr).C++对象模型 一.虚函数表指针(vptr)及虚基类表指针(bptr) C++在布局以及存取时间上主要的额外负担是由virtual引起的,包括: virtual function机制:用以支持一个有效率的"执行期绑定": virtual base class:用以实现多次在继承体系中的基类,有一个单一而被共享的实体. 1.虚函数表指针 C++中,有两种数据

单继承与多继承中的虚函数表和虚函数指针

首先,我们了解一下何为单继承,何为多继承?? 单继承:一个子类只有一个直接父类. 多继承:一个子类有两个或多个直接父类. 单继承中的虚函数表分析: 示例程序: #include <iostream> using namespace std; typedef void(*FUNC)(); class Base { public: virtual void func1() { cout << "Base::func1()" << endl; } virt

关于C++中虚函数表存放位置的思考

其实这是我前一段时间思考过的一个问题,是在看<深入探索C++对象模型>这本书的时候我产生的一个疑问,最近在网上又看到类似的帖子,贴出来看看: 我看到了很多有意思的答案,都回答的比较好,下面贴出一些具有代表性的: Answer 1: Answer 2: 我们都知道,虚函数是多态机制的基础,就是在程序在运行期根据调用的对象来判断具体调用哪个函数,现在我们来说说它的具体实现原理,主要说一下我自己的理解,如果有什么不对的地方请指正在每个包含有虚函数的类的对象的最前面(是指这个对象对象内存布局的最前面,

虚函数表存放在哪里

from: here 1.虚函数表是全局共享的元素,即全局仅有一个. 2.虚函数表类似一个数组,类对象中存储vptr指针,指向虚函数表.即虚函数表不是函数,不是程序代码,不肯能存储在代码段. 3.虚函数表存储虚函数的地址,即虚函数表的元素是指向类成员函数的指针,而类中虚函数的个数在编译时期可以确定,即虚函数表的大小可以确定,即大小是在编译时期确定的,不必动态分配内存空间存储虚函数表,所以不再堆中. 根据以上特征,虚函数表类似于类中静态成员变量.静态成员变量也是全局共享,大小确定. 所以我推测虚函

C++中的虚函数以及虚函数表

一.虚函数的定义 被virtual关键字修饰的成员函数,目的是为了实现多态 ps: 关于多态[接口和实现分离,父类指针指向子类的实例,然后通过父类指针调用子类的成员函数,这样可以让父类指针拥有多种形态,所以称之为多态] 二.虚函数表 该表为一个类的虚函数的地址表,用于解决继承和覆盖的问题 1.拥有虚函数的类才有虚函数表 2.虚函数表属于类,然后类的所有对象通过虚函数表指针共享类的虚函数表 3.虚函数表的作用:当使用父类指针来操作子类对象时,虚函数表就像一个地图一样,指明了实际所应该调用的函数 4

虚函数表指针vptr的测试

类的虚函数调用是通过虚函数表实现的.所谓虚函数表,是编译器自动为一个带有虚函数的类生成的一块内存空间,其中存储着每一个虚函数的入口地址.由于函数的入口地址可以看成一个指针类型,因此这些虚函数的地址间隔为四个字节.而每一个带有虚函数类的实例,编译器都会为其生成一个虚函数指针——vptr,在类的对象初始化完毕后,它将指向虚函数表. 这个vptr指针将位于类对象的首部,即作为第一个成员变量,处于类对象代表的内存块的前四个字节中.为了便于理解和复习,在此将其内存结构以图示之. 查阅资料得知,C++标准并

C++学习 - 虚表,虚函数,虚函数表指针学习笔记

虚函数 虚函数就是用virtual来修饰的函数.虚函数是实现C++多态的基础. 虚表 每个类都会为自己类的虚函数创建一个表,来存放类内部的虚函数成员. 虚函数表指针 每个类在构造函数里面进行虚表和虚表指针的初始化. 下面看一段代码: // // main.cpp // VirtualTable // // Created by Alps on 15/4/14. // Copyright (c) 2015年 chen. All rights reserved. // #include <iostr