资讯专栏INFORMATION COLUMN

c++中virtual关键字的作用与Java中多态的一点对比

Sleepy / 2038人阅读

摘要:结论这个关键字的发挥作用是在子类去继承父类的时候。在中,作者也说了,尽可能的在申明,传参,返回值的时候使用父类和接口,而不要使用实现类。

动机

最近一直在使用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 strList = new ArrayList();    //这样是耿直的写法
List strList = new ArrayList();    //这样更好,因为你可以换后面这个new

// 返回值和参数也是一样,一般能使用接口就尽量使用接口,而不要写死成实现类,这样带来更大的灵活性
public List buildStrList(List raw, AnyInterface interf) {
    // do xxxx
}
总结

virtual关键字修饰的方法在子类继承实现后,就可以达到多态的目的(使用父类的指针依然可以调用到子类的实现)。

Java中不需要这个关键字来达到多态,覆写方法自带这个功能。

参考

c++ ref: polymorphism

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/64969.html

相关文章

  • C++基础语法(五)继承——万字总结,干货满满

    摘要:继承方式继承方式限定了基类成员在派生类中的访问权限,包括公有的私有的和受保护的。所以子类给父类引用赋值也是可以的,相当于给子类对象中继承的父类部分起了别名。如图成员函数也是如此,当子类与父类具有函数名相同的函数时,还是符合就近原则。 ...

    smartlion 评论0 收藏0
  • Java编程那些再熟悉不过知识点(持续更新)

    摘要:语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。有针对不同系统的特定实现,,,目的是使用相同的字节码,它们都会给出相同的结果。项目主要基于捐赠的源代码。 本文来自于我的慕课网手记:Java编程中那些再熟悉不过的知识点,转载请保留链接 ;) 1. 面向对象和面向过程的区别 面向过程 优点: 性能比面向对象高。因为类调用时需要实例...

    taowen 评论0 收藏0
  • C++多态底层刨析(虚函数指针,虚函数表)

    摘要:当子类继承了父类并且子类重写了父类的虚函数之后,我们可以看到此时子类中虚函数指针对应的虚函数表中存的是子类经过重写的函数了。 前言:相信小伙伴们在学习到C++面...

    callmewhy 评论0 收藏0
  • 谈谈Java面向对象

    摘要:也就是说,一个实例变量,在的对象初始化过程中,最多可以被初始化次。当所有必要的类都已经装载结束,开始执行方法体,并用创建对象。对子类成员数据按照它们声明的顺序初始化,执行子类构造函数的其余部分。 类的拷贝和构造 C++是默认具有拷贝语义的,对于没有拷贝运算符和拷贝构造函数的类,可以直接进行二进制拷贝,但是Java并不天生支持深拷贝,它的拷贝只是拷贝在堆上的地址,不同的变量引用的是堆上的...

    ormsf 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<