前言
关于虚函数,原理都明白,分为纯虚函数和虚函数,其中纯虚函数需要在虚函数后面加上=0
,而虚函数不用,虚函数和纯虚函数的区别是虚函数是基类创建class,并且在里面创建一个虚函数,子类继承虚函数并且使用,而纯虚函数除了继承还必须要覆盖他,如下
#include <iostream>
using namespace std;
class Base{
public:
Base() = default;
virtual void Print(){cout << "non-pure virtual func!! i am in Base" << "\n";}
virtual void PurePrint() = 0;
};
void
Base::PurePrint(){
cout << "Pure virtual func !! i am in Base" << "\n";
}
class Derive : public Base{
public:
void PurePrint() override;
};
void
Derive::PurePrint(){
cout << "Pure virtual func !! i am in Derive" << "\n" ;
}
int main()
{
Derive d;
d.Print();
d.PurePrint();
return 0;
}
打印如下
non-pure virtual func!! i am in Base
Pure virtual func !! i am in Derive
假如我们去掉Derive
关于虚函数的重新定义就会报错
override
是C++11的新keyword,假如在Derive类的里面的一个继承而来的虚函数后面定义了override,代表告诉编译器这个函数被重写了,编译器就回去找这个函数假如没找到就会报错undefined reference to `vtable for Derive’
位于protected中的虚函数
上面已经讲述了虚函数和纯虚函数基本的使用,这里我们加个限定,我们的虚函数位于protected中
在开始本章的时候我们先问一个问题
虚函数什么时候应该在private
,什么时候应该在protected
,什么时候应该在public
中?
我们经常使虚函数位于private
中,这让我们尝尽了苦头,所以我们必须要把他暴露出去,在之前也许我们学到过数据应该位于private
中,除非我们像是在写C风格代码的struct
一样,把数据都放置于struct
中,而且并不准备封装
通常来说程序员会选择在基类中直接使用public virtual functions
,并且定义其customizable行为,我们把它称为simultaneously part,(一个simultaneously part代表一个方法,和这个方法有关联的需要特定实现的多个方法,由具体继承的class实现)1
class Widget{
public:
virtual int Process( Gadget& );
virtual bool IsDone();
};
后续具体实现由继承的类实现
但是这样设计有一个问题,问题就出现在这个simultaneously part,每一个虚函数都会完成2个步骤,分别是在类封装后暴露一个外部结构(因为是public),还有一个是确定实现的细则(因为是虚函数需要override,如果需要的话),综上所属public virtual func
有2个明显不同的工作,他并没有好的分离他们,所以我们要考虑另外一种方法
什么样的封装才是最好的?对obj用户而言,里面的实现细节不可见,上述的代码暴露了2个接口给用户,我们最好只暴露一个接口,其他的工作内部实现,而且在用这个虚函数的时候还要考虑他是虚函数我们还要override他,我们想分离这2个功能如下
class Widget{}
public:
int Process( Gadget& );
bool IsDone();
//...
//...
//...
private:
virtual int DoProcessPhase1( Gadget& );
virtual int DoProcessPhase2( Gadget& );
virtual bool DoIsDone();
//...
//...
//...
;
这里再普及一个概念,编译时,运行时,他们是以生成二进制文件为分界线,在生成二进制文件之前是编译时,生成二进制文件后是运行时,我们一个代码比如a[10],是编译时的,因为要把a[10]放到栈上开辟10个空间,虽然栈的伸缩都是发生在运行时,但是我们a[10]是确定的,我们就用一个相对地址,在运行的时候进行地址转换即可,不用在运行的时候再开辟10个空间,大大的减小了运行速度2
基类中的虚函数在private里面确定其可以访问吗?
答案是可以的,因为C++是访问控制而不是Visbility 控制,所以私有函数是不能访问不代表不能看的找,所以私有虚函数可以被派生类给overridden3
再看上述代码他的确做到了分离暴露接口和虚函数override的功能,首先public里面的函数我们只暴露给对象(在封装之后),这个函数具体执行的内容是虚函数中的内容,当一个class继承他的时候,不管怎么继承都会继承public里面的函数,然后protected里面的虚函数也可以被子类override