C++虚函数

来源:互联网 发布:mindnode pro for mac 编辑:程序博客网 时间:2024/06/09 20:22

虚函数是动态多态性的基础,其调用的方式是动态联编(又称晚期联编,简单解释为只有在程序运行时才决定调用基类的还是子类的,系统会根据基类指针所指向的对象来决定要调用的函数);

非虚函数与其相反,是静态联编(调用已经在编译时期就决定了;在编译时期,系统已经根据指针所属的类型确定了要调用的函数)。

class shape
{
public:
    shape(){};
    void draw()
    {
        画图形;
    }
};
 
class rectangle : public shape
{
public:
    rectangle(){};
    void draw()
    {
        画方形;
    }
};
 
class round : public shape
{
public:
    round(){};
    void draw()
    {
        画圆形;
    }
};
 
void main()
{
    shape * s;
 
    s = new rectangle();
    s->draw();
 
    s = new round();
    s->draw();
}

我主程序中的意思是要画方形和画圆形,可执行的结果都是画图形(叫你两儿子出来,怎么出来的都是你啊),这很明显不符合我们的要求和多态的要求。所以虚函数必须应运而生。

class shape
{
public:
    shape(){};
    virtual void draw()    //注意这里设置为虚函数
    {
        画图形;
    }
};
 
class rectangle : public shape
{
public:
    rectangle(){};
    void draw()
    {
        画方形;
    }
};
 
class round : public shape
{
public:
    round(){};
    void draw()
    {
        画圆形;
    }
};
 
void main()
{
    shape * s;
 
    s = new rectangle();
    s->draw();
 
    s = new round();
    s->draw();
}

这就满足我们了,一个是画方形,一个是画圆形。
发现:即使不在基类shape当中把draw设置为虚函数,我们只要直接在主函数当中直接声明子类的对象,调用draw,这种情况下是符合多态的思想的,

void main()
{
  round r;
  r.draw();
}

这就有疑问了,为什么还需要virtual关键字(虚函数)的存在呢?

上面的例子只是一个两个派生类,如果shape有成百上千个派生类,难道我们调用派生类的draw的时候都声明一个派生类的对象吗?很明显不可能,所以虚函数的情况下,我们只要声明一个shape的指针就好了。


如何实现动态联编?

编译器是如何针对虚函数产生可以在运行时刻确定被调用函数的代码呢?也就是说,虚函数实际上是如何被编译器处理的呢?Lippman在《深度探索C++对象模型》中的不同章节讲到了几种方式,这里把标准的方式简单介绍一下。

这里所说的“标准”方式,也就是所谓的“VTABLE机制。编译器发现一个类中有被声明为virtual的函数,就会为其搞一个虚函数表,也就是VTABLEVTABLE实际上是一个函数指针的数组,每个虚函数占用这个数组的一个slot。一个类只有一个VTABLE,不管它有多少个实例。派生类有自己的VTABLE,但是派生类的VTABLE与基类的VTABLE有相同的函数排列顺序,同名的虚函数被放在两个数组的相同位置上。在创建类实例的时候,编译器还会在每个实例的内存布局中增加一个vptr字段,该字段指向本类的VTABLE


另外转一篇文章C++ 虚函数表解析http://blog.csdn.net/haoel/article/details/1948051

0 0
原创粉丝点击