Android C++ 系列:C++ 最佳实践 1 虚函数
1. 背景
C++多态的核心技术基础就是虚函数,虚函数允许我们使用同样的基类指针调用同一个方法的不同实现版本。我们 Android 使用 Java 开发过程中,方法重写技术自动实现了多态,C++角度可能更繁琐一些,本文从 Java 程序员思维角度来阐述 C++虚函数及开发过程一些准则。
2. 什么是虚函数
在 Java 中我们实现继承结构的两个类:
Java 直接覆盖后就会自动的调用到实际类的对应方法,但是 C++中不行。
打印的结果是:
就算是 b 指向了 Sub 对象,但是打印的还是 Base 的方法,没有自动绑定,写惯了 Java 后马上慌了,怎么破。这里就需要用到我们今天的主人公”虚函数“了,在方法声明之前加上 virtual 这个函数就变成虚函数。我们在 Base 的 action 方法声明前加上 virtual 打印结果就变成了:
当前仅当对通过指针或引用掉用虚函数时,才会在运行时解析该调用,也只有这种情况下对象的动态类型才有可能与静态类型不同。
3. 虚函数注意点
基类通过在其成员函数的声明语句之前加上关键字 virtual 使得该函数执行动态绑定。如果基类把一个函数声明成虚函数,则该函数在派生类中隐式地也是虚函数。派生类可以在它覆盖的函数前使用 virtual 关键字,但是不是强制的。
如果我们想将某个类作基类,则该类必须已经定义而非仅仅声明。因为派生类中包含并且可以使用它从基类继承而来的成员,为了使用这些成员,派生类当然要知道它们是什么。一个类不能派生它本身。
如果在基类中含有一个或多个虚函数,我们可以使用 dynamic_cast 请求一个类型装换,这个转换的安全检查在运行时执行。同样,如果我们已知某个基类向派生类的转换是安全的,我们可以使用 static_cast 来强制覆盖掉编译器的检查工作。
如果我们不使用某个函数,则无须为该函数提供定义,但是我们必须为每一个虚函数都提供定义,而不管它是否被用到,因为连编译器也无法确定到底会使用哪个虚函数。
派生类中虚函数的返回类型必须与基类函数匹配。例外的情况是,当类的虚函数返回类型是类本身的指针或引用时,不需要遵循这个规则。如果 Impl 由 Base 派生,则基类的虚函数可以返回
Base*
,而派生类的对应函数可以返回Impl*
,这样的返回类型要求从 Impl 到 Base 的转换是可访问的。我们可以把某个函数指定为 final,如果已经把函数指定为 final,则之后任何尝试覆盖该函数的操作都将引发错误。
如果虚函数使用默认实参,则积累和派生类中定义的默认实参最好一致。
如果我们不想对虚函数的调用执行动态绑定,可以使用作用域运算法强迫执行虚函数的某个特定版本。通常情况,只有成员函数(或友元)中的代码才需要使用作用域运算法来回避虚函数的机制。在 Java 中子类调用父类被重写的方法直接
super.xxx()
即可,在 C++中要强制使用某个版本的虚函数,必须使用该版本对应的类加::
作用域,如Base::action()
。我们 Java 中如果想调用父类的父类的被覆盖的方法就有点困难了,这一点 C++反而更有优势了。如果一个派生类虚函数需要调用它的基类版本,但是没有使用作用域运算符,则在运行时该调用将被解析为对派生类版本自身的调用,从而导致无限递归。
4. 总结
作为 Android 方向 C++系列文章,不仅介绍 C++相关的知识,Android 主要开发语言是 Java,文章中尽量会对比 Java 和 C++实现的一些不同,以及一些优劣对比。本文介绍了 C++多态的基础虚函数,对比了和 Java 版本实现多态的差异。
版权声明: 本文为 InfoQ 作者【轻口味】的原创文章。
原文链接:【http://xie.infoq.cn/article/4cb1a1f29778e08c09c132673】。文章转载请联系作者。
评论