C++中的接口继承和实现继承
不少人认为,C++中是不存在接口继承的,只有Java、C#这类语言才提供了相应的语法支持。设计模式
可是,如同鲁迅说过的某句名言:世上本没有接口继承,用的人多了,才有了接口继承。C++中依然能够实现接口继承,只是形式上稍有不一样罢了。app
C++中的继承基于一个事实:父类定义的成员函数会一直被子类继承(包括被子类隐藏的部分)。函数
而父类中提供的函数能够有三种:1)普通成员函数 2)普通虚函数 3)纯虚函数。这三种函数类型表明了三种继承设计模式。spa
一个简单的实例代码以下:设计
04 |
virtual void Draw() = 0; |
05 |
virtual int GetError(); |
09 |
class Rectangular : public Shape |
14 |
class Circle : public Shape |
普通成员函数由父类声明且实现,子类应继承接口以及强制性的实现。code
这几乎是最多见的一种函数类型,表明了典型的”is-a”继承设计模式。对象
ps:所谓的”is-a”设计模式,指的是”everything that applies to base classes must also apply to derived classes”继承
示例中,函数GetId严格遵照”is-a”模式。由于每一个子类本质都是一个Shape对象,都有一个惟一的ID接口
普通虚函数能够在父类中有默认的实现,而这个默认实现能够由子类继承。
子类也能够选择重写虚函数以实现多态性。资源
因此,普通虚函数在继承设计中表示派生类必须支持此接口,可是否重写,由派生类本身决定。
如同每一个子类对象都应该有一个报错函数。可是函数可使用父类提供的默认实现(提示简单的出错信息,而后清理资源),也能够选择本身实现(每一个子类有本身的错误语义)
纯虚函数会使得父类自动成为不可实例化的抽象类。并且每一个继承的子类必须强制自行重写。
因此,纯虚函数表示子类继承父类的函数接口,而且必须本身具体实现该函数。
即从这个角度上看,纯虚函数表明的就是接口继承。
实例代码中,父类将Draw声明为纯虚函数。这代表每一个具体的子类都应该有Draw函数,而且须要本身实现(每一个具体子类的Draw实现应是不一样的)。
对于纯虚函数,有一个有意思的特性:纯虚函数能够有实现代码
之因此说这个特性有意思,是由于拥有纯虚函数的类不能实例化而且纯虚函数指定的是接口继承,子类仍然须要本身实现函数。
这就引起了一个问题:如何调用这个纯虚函数的默认实现版本?解决的方法是显式调用
1 |
Shape* p = new Rectangular; |
4 |
// invoke defaulat implementation of the pure-virtual function |
7 |
// doing something later |
那么这种让人以为操蛋的trick有没有什么应用呢?
假设你有一个父类F,定义了一个普通虚函数,子类A,B都使用默认的虚函数实现。可是某天你须要增长一个新的子类C,可是这个子类不能使用默认的实现,必须重写。
不幸的是,你忘了重写这个函数,因此编译器为你调用了默认实现,因而意外的结果让你蛋碎了一地。
很明显,使用带有实现的纯虚函数就能够解决这个问题。纯虚函数会强制要求你重写虚函数,而你也能够在须要默认实现时经过显式调用完成相应工做。
不过须要这种trick的状况至关罕见,并且多半是设计出了问题或者彻底能够人为避免。