final finally finalize

JScript 8.0
final 修饰符

class CBase {
   final function methodA() { print("Final methodA of CBase.") };
   function methodB() { print("Non-final methodB of CBase.") };
}

class CDerived extends CBase {
   function methodA() { print("methodA of CDerived.") };
   function methodB() { print("methodB of CDerived.") };
}

var baseInstance : CBase = new CDerived;
baseInstance.methodA();
baseInstance.methodB();


该程序的输出显示 final 方法未被重写
Final methodA of CBase.
methodB of CDerived.

****************************************************************************************
C#从C++那里继承了“virtual”关键字,而这一点跟Java只是形式上的不同,C#中非virtual方法在很大程度上就相当于Java中的final方法
运行时动态绑定被调过程的“虚拟方法”(virtual method)机制是实现多态的关键技术.
C++JavaC#(按出生年月排列,上同,下同)作为三种主流的支持对面向对象的程序设计语言,自然都提供了这种动态的方法绑定机制,在这个问题上三兄弟没有谁是含糊的
但当“虚拟”(virtual)和“私有”(private)碰在一起时,这几种语言在处理上却有所不同,本文的故事就是从一段小程序说起的
我们来看下面这段短小的C++程序

// Test.cpp
#include <iostream>
using namespace std;
class Base
{
public:
    
void f() 
{

        g();
    }

private:
    
virtual void g() {
        cout 
<< "Hi, MorningStar! I am g() of Base!." << endl;
    }

}
;
class Derived : public Base
{


private:
    
virtual void g() 
{
        cout 
<< "Hi, MorningStar! I am g() of Derived." << endl;
    }

}
;
int main()
{
    Base 
*pB = new Derived();
    pB
->f();
    delete pB;
    
return 0;
}



 

程序很简单,我们在基类“Base”中,通过公有方法f调用了虚拟的私有方法g,而继承自BaseDerived类中覆写(override)了私有的方法g

先不考虑其他,语法上有问题吗?没有;编译能通过吗?能。那运行结果是什么呢?既然编译能通过,那么结果也就能猜到了,应该是:

Hi, MorningStar! I am g() of Derived.       
没错,VC6.0VC.net 2003gcc都是这个结果。从结果来看,对私有方法g的调用在运行时绑定到了对象实际类型(Derived)的方法上,这符合虚拟函数的语义。

我们知道,在Java中,没有“virtual”这么个关键字,默认就是virtual,不可被覆写的才需要加关键字“final”。Java中,对已被覆写的方法如果想调用其父类的方法,则需通过特殊的关键字“super”调用。好了,不多说Java语法了,我们看下面一段类似的Java程序:

 

//Derived.java



class Base {



    
public void f() {



        g();



    }




    
private void g() {



        System.out.println(
"Hi, MorningStar! I am g() of Base.");



    }

}


public class Derived extends Base {



    
private void g() {



        System.out.println(
"Hi, MorningStar! I am g() of Derived.");



    }




    



    
public static void main(String[] args) {



        Base b 
= new Derived();



        b.f();



    }




}



 

使用命令行

javac Derived.java

编译,然后

Java Derived

运行,应该不会有什么问题(偶是在Eclipse里头直接运行的:P)。结果为:

Hi, MorningStar!, I am g() of Base.

 

虽然程序类似,但这两中不同的语言产生的结果却不一样。

翻了翻C++98标准(ISO/IEC 14882)的相关章节,没有找到任何关于此种情况的特殊描述,惟一涉及到虚拟函数访问的第11.6节:Access to virtual functions,说的又不是这种情况。由此可见,C++语言中仅仅分别规定了publicprotectedprivate三者的访问控制问题和virtual方法的动态绑定问题,而对于他们的组合,没加任何限制,换句话说,前后是两种无关的机制,访问控制专门负责访问权限的问题,不管方法是不是虚拟;而虚拟只管延迟绑定的问题,不管公有、保护还是私有。于是,前面的C++程序输出那个结果也就不足为怪了。

然而,从面向对象的角度考虑,基类中私有的东西对外界、对继承类都是不可见的,继承类根本不应该知道基类中任何私有的东西,于是继承、覆写也应无从谈起才对,即使方法重名,那也应该仅仅看作一种巧合,Java就是这么做的。

Java中,private天生就是final的,我想Java的设计者至少有过上面的考虑:既然是私有,就不可见,见都见不到,何谈覆写?所以,在Java中,我们讨论继承和多态都是针对类的对外接口,包括puclic方法、protected方法和默认的friendly方法,而private是不被纳入考量的,从某种意义上,Java中的private方法纯粹是为方法而方法,是一种组织代码使之更清晰,更易维护的手段而已。

但如果仅从语法语义上考虑,而不是从面向对象的理论上考虑,或许C++更厚道一些——既然没有限制,那么private也可以virtual,既然可以virtual,那也就应该享受virtual级的待遇。而Java也没什么virtualvirtual,没有任何语法痕迹便闷声地规定:public/protected/friendly的是这样,而private的是那样。

 

 

最后,让我们来看看时代的新宠,C#是怎么做的呢?我们使用Visual Studio.net 2003新建Console应用程序,添加C#源文件,然后也可以写类似的代码:

 

//Test.cs



using System;



namespace Test



{



    
class Base 



    
{



        
public void f() {



            g();



        }




        
private virtual void g() {



            Console.WriteLine(
"Hi, MorningStar! I am g() of Base.");



        }




    }





 


    
class Derived : Base 



    
{



private override void g() 
{
            Console.WriteLine(
"Hi, MorningStar! I am g() of Derived.");
        }

public static void Main(String[] args) {

            Base b 
= new Derived();
            b.f();
        }

    }

}



 编译一下。完蛋了,编译通不过!

...\Test.cs(14): Test.Base.g() : 虚拟成员或抽象成员不能是私有的

...\Test.cs(22): Test.Derived.g() : 虚拟成员或抽象成员不能是私有的 

看来还是C#够狠,语法上拒绝虚拟私有——那就没啥输出结果可以拷贝过来了。

C#C++那里继承了“virtual”关键字,而这一点跟Java只是形式上的不同,C#中非virtual方法在很大程度上就相当于Java中的final方法。只是Javafinal还用于指定类本身可否被继承,C#使用另外一个关键字“sealed”做这件事。

相对来讲,还是C++最灵活,它至少没有限制这样一种可能:基类允许派生类重新实现某个方法,却不允许派生类直接调用基类的实现,即:我可以直接用你的(运行时绑定),但你不可以直接用我的——这一点跟protect/protected不一样。而“重写”与“调用”毕竟还是两码事,既然是两码事,分开考虑就不能说没有必要。
 
总结:

C++从语法到语义上都支持可被覆写的虚拟私有成员;

Java语法上看不出什么痕迹,但语义上不存在可被覆盖的私有成员;

C#从语法上直接拒绝私有方法成为虚拟(可被覆写)

就C++来说,如果你想定义一个需要派生类实现的虚拟函数,但是又不允许派生类或者client直接调用,例如基类需要进行一些前置处理等,那么 private virtual就是最好的选择.
 

posted on 2007-11-13 10:35  simhare  阅读(410)  评论(0)    收藏  举报

导航