动机
最近一直在使用C++写win32程序,用了一些库,里面提供的类和demo各种是virtual
这个关键字,一直不是很明白到底是啥用,于是查看了一些文档,写小程序来实验它的作用。
结论
virtual
这个关键字的发挥作用是在子类去继承父类的时候。比如:
class Person{public: void foo1() { // do ... } virtual void foo2() { // do ... }};
像上面的代码,如果类Person
就一直被实例化使用,但是没有类去继承它的话,那么这个virtual
实际上并没有什么卵用。foo2()
方法和foo1()
是一样的。
当它被继承的时候,有两种情况,覆写(override)这个foo2()
方法,或者不覆写它。比如这样:
class Student : public Person{public: void foo2() { // do something.. }};class Teacher : public Person{public: // no override};
然后我们使用的时候,如果是子类的实例,调用foo2()
方法,理所当然是执行子类中所定义的foo2()
方法体。但是当将这个子类的实例强制转型成父类的实例(指针),再去执行foo2()
方法的时候,对应的两种情况:子类实现了父类中virtual
方法的,调用子类的方法;子类中没有override
的,仍然是调用父类中的实现(这不是废话么……)
列个表格大概是这样:
// 大前提是父类中有个`virtual`方法`foo2()` 是否override foo2() 调用子类实例的foo2() 强转成父类后调用foo2()子类1 是 执行子类1的foo2() 执行子类1的foo2()子类2 否 执行父类的foo2() 执行父类的foo2()// 另一种情况// 大前提是父类中有个方法`foo2()`,但是没有virtual关键字修饰 是否override foo2() 调用子类实例的foo2() 强转成父类后调用foo2()子类1 是 执行子类1的foo2() 执行父类的foo2()子类2 否 执行父类的foo2() 执行父类的foo2()
与Java的对比
我的感觉好像Java自带这个多态的特性,不需要用什么关键字修饰,某个实例转换成父类后调用方法,默认就会调用子类的实现(如果有的话)。写了个小demo实验了一下,果然如此。
public class Main { public static void main(String[] args) { Person p = new Person(); p.foo(); // output: Person foo Student s = new Student(); s.foo(); // output: Student foo Person ps = s; ps.foo(); // output: Student foo } static class Person { public void foo() { System.out.println("Person foo"); } } static class Student extends Person { public void foo() { System.out.println("Student foo"); } }}
在《Effective Java 2e》中,作者也说了,尽可能的在申明,传参,返回值的时候使用父类和接口,而不要使用实现类。
大概是这样:
ArrayListstrList = new ArrayList (); //这样是耿直的写法List strList = new ArrayList (); //这样更好,因为你可以换后面这个new// 返回值和参数也是一样,一般能使用接口就尽量使用接口,而不要写死成实现类,这样带来更大的灵活性public List buildStrList(List raw, AnyInterface interf) { // do xxxx}
总结
virtual
关键字修饰的方法在子类继承实现后,就可以达到多态的目的(使用父类的指针依然可以调用到子类的实现)。Java中不需要这个关键字来达到多态,覆写方法自带这个功能。