动机
最近一直在使用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》中,作者也说了,尽可能的在申明,传参,返回值的时候使用父类和接口,而不要使用实现类。
大概是这样:
ArrayList<String> strList = new ArrayList<String>(); //这样是耿直的写法
List<String> strList = new ArrayList<String>(); //这样更好,因为你可以换后面这个new
// 返回值和参数也是一样,一般能使用接口就尽量使用接口,而不要写死成实现类,这样带来更大的灵活性
public List<String> buildStrList(List<String> raw, AnyInterface interf) {
// do xxxx
}
总结
virtual
关键字修饰的方法在子类继承实现后,就可以达到多态的目的(使用父类的指针依然可以调用到子类的实现)。Java中不需要这个关键字来达到多态,覆写方法自带这个功能。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。