学习过C++类的继承与多态的时候,但是很多的童鞋回过头来都忘记之中关于虚函数和纯虚函数的细节,甚至将他们混淆。

§虚函数

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

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

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的指针就好了。

 

本来只想看看书就过了,但是最近在看《设计模式》和《effective C++》这两本书,其中将到很多关于C++中封装,继承和多态的思想,所以不得不做一些笔记。

 

关于虚函数,在多态当中,一定要将基类的析构函数设置为虚函数并将其实现,只有这样,才能够达到按对象构造的逆序来析构对象;否则,析构的时候,只会析构基类的那一部分,那么派生类那一部分就无法成功析构了。

 

class shape
{
public:
shape(){};
virtual void draw() = 0;
virtual ~shape(){cout << "shape destruction" << endl;}
};
class rectangle : public shape
{
public:
rectangle(){};
void draw()
{
}
~rectangle(){cout << "rectangle destruction" << endl;}
};
class round : public shape
{
public:
round(){};
void draw()
{
}
~round(){cout << "round destruction" << endl;}
};
void main()
{
shape * s;
    s = new rectangle();
s->draw();
delete s;
s = new round();
s->draw();
delete s;
}

 


 

§纯虚函数

 

在虚函数的中举了shape的例子,里面有个draw函数,但是如果具体去实现 的话,draw shape?这实现起来很模糊,如果将shape基类实例化的话,就搞不清draw什么了(画出乱七八糟的东西,这不是客户需要的东西,客户需要的是具体 的图形)。因此有必要在基类shape当中,将draw的定义(实现)除去,而将它的定义(实现)留给派生类。

 

class shape
{
public:
shape(){};
virtual void draw()=0;    //纯虚函数
};
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();

 

 

 

§总结

 

看 了上面的代码是不是很有感触,回忆起来了吧,C++里面的抽象类与java里面的接口很类似,不过C++抽象类允许有成员变量的出现。没有谁强迫你说抽象 类里面的方法不能在抽象类里面实现,但是如果你确实在抽象类里面实现了,为它提供一份实现代码,C++并不会发出怨言,但是调用它的唯一途径就是“调用时 明确指出其class的名称”。但是你发现没有,即便如此,抽象类里面实现纯虚方法(函数)也是多余的,用途也很有限。

 

 

 

本文完。

 

捣乱小子

 

posted on 2013-03-20 11:18  爱哎唉  阅读(60)  评论(0)    收藏  举报