虚函数、纯虚函数和接口的实用方法和意义
从理论上来说,这三个概念很容易背的滚瓜烂熟,但是从大学毕业到现在,我都没真正搞明白这三个东西的出现,究竟是为了做到什么事情。
也许之前我很少写代码,更很少写面向对象的代码,即使有写多半也很容易写回到面向过程的老路上去。在写面向过程的代码的时候,根本不管什么函数重载和覆盖,想到要什么功能就变得法子的换个函数名字,心里想想:反正函数重载本质也就是入栈了两个不同的函数。
知道后来我才慢慢了解,这些概念的出现,完全就不是为了编程的功能实现,而是编程的易用和扩展,准确的来说是方便再次开发而提出的一种标准而已。如果仅仅只要写个自己用的功能性的程序,那的确可以用不到上面这些麻烦的东西。
回过头来讲,让我了解标题这三个概念的实际用处,还是在于我这第四次重写毕业论文的代码,将它改写成面向对象的时候,才理解的。在面向对象设计的过程中,类是从抽象逐渐具体起来的,父类可以是非常非常抽象的东西,而最终实例化的子类就非常具体了。在这个继承的过程中,不断的对父类进行填充丰富,最终得到的子类就是有血有肉的 - 我的理解。
虚函数的意义,就在于定义了一个从最早的父类,到最后的子类,都必须具备的一个功能(函数),只是在不断的进化(继承)中,这个功能会略微发生改变。通过虚函数,我们在调用不同的衍生类的时候,可以拥有不同的功能。然后我会说:这么麻烦,干脆每个继承类都重写命名一个函数么算了,只要知道重命名的函数有这个功能就行了不是?理论上来说,完全可以,在一个父类和其继承类不多的项目中,这么做完全可以,只要你自己能熟记或者找到这个重命名函数是干嘛用的;但是在大一点的项目中,由于类中的函数成百上千,恐怕你就会为此疯狂。另外还有一点,是重命名函数无法做到的,这一点我会在纯虚函数中一并解释。
纯虚函数,就是虚函数了以后,末尾还要加=0的那一类函数。我一直没想通的是,既然这个函数完全没有实现方法,那么定义这个函数有个蛋用啊?我也曾经试着在网上搜索过纯虚函数的意义和作用,回答大多千篇一律照本宣科。于是我渐渐的也就无视这个纯虚函数了。直到现在我开始写一个PSO算法的时候,才发现天哪这居然是一个完全不可或缺的东西!如果说虚函数还可以用重命名作为另外一种解决方法,那么纯虚函数则是没有第二种可以替代的方法。我可以拿一个非常简单的代码说明一下:
class test{
public:
virtual void print();
virtual void order()=0;
int array[20];
};
上面声明了一个非常简单的类,它只有两个函数,其中一个是虚函数:打印,另外一个是纯虚函数:排序。其中打印函数的定义如下
void test::print(){
order();
printf("打印结果: ");
for(int i=0; i<20; i++)
printf("%d ", array[i]);
}
在这个打印函数中,调用了order函数对array进行了排序,然后输出结果。问题是:我根本不知道order函数是什么算法,或者说order函数因人而异,所以无法确定!于是网上照本宣科的内容就出来:当函数没有实现方法或者需要子类来定义实现方法的时候,可以在父类中定义纯虚函数。就是这么简单!于是当不同的子类继承这个父类的时候,定义不同的实现方法,那么实例化这个子类的时候,这个纯虚函数就有了不同的方法。这也解释了为什么包含纯虚函数的抽象类为什么不能实例化,因为它中间有函数根本不知道是怎么个实现!当然我们可以用其他方法避免使用纯虚函数,比方说在子类中重写print方法,但是这样一来等于除了order函数代码以外所有的代码都要重新复制一遍,当继承类越来越多的时候,要修改print等于这一堆继承类都要修改,会疯的!所以说纯虚函数是一个很神奇的用法,也是简化了编程使得面向对象的方法更加灵活。
至于接口,这是一个只有JAVA中才用到的概念,C++中不存在接口,与接口相似的是:抽象类。因为JAVA不允许多重继承类,但可以继承多个接口。关于接口,在我编写JAVA SERVLET的时候,碰到过一个httpservlet,用户需要为doget和dopost等函数编写实现方法。而这些函数就可以看成是纯虚函数,它在HTTPservlet也类似于上述代码的order函数,有着在局部函数中的作用。
面向对象编程确实很有意思,虽然从某种程度上来说,和面向过程也差不多,但是灵活多变的设计方法,也许也是C++(面向对象)比C(面向过程)强大的地方