关于C++虚函数多态的剖析

来源:互联网 发布:java动力节点培训坑人 编辑:程序博客网 时间:2024/06/01 08:13
 

C++中,多态是通过虚函数来实现的。如下:

//含有纯虚函数的类叫抽象类,抽象类不能被实例化,抽象类的继承类如果不该写纯虚函数,//那么该类仍然是抽象类

class shape

{

    public:

        virtual void display() = 0;

};

class rect: public shape

{

    public:

        virtual void display()

        {

            cout<<"rect"<<endl;

        }

};

class circle: public shape

{

    public:

        virtual void display()

        {

            cout<<"cricle"<<endl;

        }

};

class square: public shape

{

    public:

        virtual void display()

        {

            cout<<"square"<<endl;

        }

};

 

int main(int argc, char* argv[])

{

    //shape  o_shape;

    rect   o_rect;

    circle o_circle;

    square o_square;

 

    shape *base_shape[3] = {//&o_shape,

                            &o_rect,

                            &o_circle,

                            &o_square,};

    for( int i = 0; i < 3; i++ )

    {

        base_shape[i]->display();

    }

 

    int stop;

    cin>>stop;

    return 0;

}

会打印:

    rect

    circle

square

由此可以看到这是以相同的指令调用了不同的函数,这种性质称为多态,编译器无法在编译时期判断到底调用哪个函数,必须在执行期才能判断,这称为动态绑定或者好后期绑定。为什么会这样,下面会通过内存布局来进行说明。

 

为了达到动态绑定的目的,C++编译器通过某个表格,在执行期间“间接”调用实际上要绑定的函数,这样的表格称为虚函数表(vtable),每一个含有虚函数的类,编译器都会为他做一个虚函数表,表中的每一个元素都指向虚函数地址。此外,为了获取该表,编译器会为类加上一项成员变量,是一个指向该虚函数表的指针(vptr)。

 

C++中的成员函数,你可以想象他为C语言中的函数,他只是被编译器改过名称,并增加了个this参数,因而可以处理调用者(C++对象)中的成员变量。所以,在一个类的内存区域中,你看不到任何与成员函数有关的东西。

每一个由此类派生出来的对象,都有一个vptr,当我们通过对象调用虚函数时,实际上是通过vptr找到虚函数表,再找出虚函数的真正地址。

奥妙就在这个虚函数表以及虚函数的调用方式,虚函数表是依据虚函数在类中的声明次序,一一填入函数指针,派生类会继承虚函数表(以及所有他可以继承的成员),当在派生类中改写虚函数时,虚函数表就受到了影响,表中的虚函数地址将不是基类虚函数地址,而变成了派生类虚函数地址。