子类在继承了父类的方法后,可以对父类已有的方法给出新的实现版本,这个动作称之为方法重写(override)。通过方法重写我们可以让父类的同一个行为在子类中拥有不同的实现版本,当我们调用这个经过子类重写的方法时,不同的子类对象会表现出不同的行为,这个就是多态(poly-morphism)。

多态性:用一个名字定义不同的函数,调用同一个名字函数,却执行不同操作,即一个接口,多种方法。

多态性是一个接口多种实现,是面向对象的核心。分为类的多态性和函数的多态性。

          类的多态性:指用虚函数和延迟绑定来实现。

          函数的多态性:函数的重载。

C++的多态性: 在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。如果对象类型是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数

 

存在虚函数的类都有一个一维的虚函数表叫做虚表。类的对象有一个指向虚表开始的虚指针。虚表是和类对应的,虚表指针是和对象对应的。

纯虚函数是虚函数再加上=0。即纯虚函数virtual void breathe()=0;即抽象类、抽象方法;(告诉编译器不需要在这的类里寻找这个方法的实现)

 

抽象类是指包括至少一个纯虚函数的类

 

多态用虚函数来实现,结合动态绑定

多态实现绑定方法:

    编译时的多态性:通过重载实现

    运行时的多态性:通过虚函数实现

 

虚方法,在方法前面加virtual 。在使用new时,创建的对象被父类接收,但是创建的对象类型和父类不同。

即在运行时才分配的类型和他们在编译时的类型是不一样的。为了让编译器知道根据这两个指针在

运行时的类型而有选择地调用正确的方法(dog::play和cat::play()),我们必须把这些方法声明为虚方法

 

#include <iostream>
#include <string>

//错误的原因:编译器编译时已经确定对象调用的函数地址,
//解决的方法(virtual):使用迟绑定(late binding)技术,编译器会在运行时再去确定对象的类型以及正确调用函数。

class Pet
{
public:
    Pet(std::string theName);

    virtual void eat();
    virtual void sleep();
    virtual void play() = 0;  //注意这里 ,抽象函数使用
protected:
    std::string name;
};

class Cat:public Pet
{
public:
    Cat(std::string theName);
    void climb();
    void play();  //这里对基类方法进行了覆盖,但如果上面不适用virtual,则在调用此方法时,
                    //不会成功被调用,而是会调用基类的方法。

                    //原因:程序编译时,编译器会检查所有的代码,在如何对某个数据进行
                    //      处理和可以对该类型的数据进行何种处理之间寻找一个最佳点。
                    //      cat dag 在编译时都是Pet类型指针,编译器就认为两个指针调用的
                    //      play()是Pet::paly()方法,因为这样最快。

};

class Dog:public Pet
{
public:
    Dog(std::string theName);

    void bark();
    void play();
};


Pet::Pet(std::string theName)
{
    name = theName;
}

void Pet::eat()
{
    std::cout<< name <<"吃东西\n";
}

void Pet::sleep()
{
    std::cout<<name<<"sleeping\n";
}
/* 使用了抽象方法,不必再写其实现
void Pet::play()
{
    std::cout<<name<<"playing\n";
}
*/

Cat::Cat(std::string theName):Pet(theName)
{
}

void Cat::climb()
{
    std::cout<<"climb";
}

void Cat::play()
{

    std::cout<<name << "paly ball\n";
}

Dog::Dog(std::string theName):Pet(theName)
{

}

void Dog::bark()
{
    std::cout<<name<<"汪\n";
}

void Dog::play()
{
    std::cout<<name<<"追猫\n";
}

int main()
{
    Pet *cat = new Cat("加菲"); 
                                //注意这里创建对象时使用的new,下面调用其方法时用->。而且
                                //也是上面使用virtual的原因:使用的指针(地址),
                                //指向了Pet的类指针。调用play时,编译时会选择最优的选择(pet中的play),指向Pet
                                //而非cat中的play

                                //或者以下,同样需要使用virtual
                                //Pet ccat("加菲");
                                //Pet *cat = &ccat;
    Pet *dog = new Dog("欧迪");

    cat->sleep();
    cat->eat();
    cat->play();

    dog->sleep();
    dog->eat();
    dog->play();

    delete cat;
    delete dog;

    return 0;
}