c++ rethink virtual

前言

关于虚函数,原理都明白,分为纯虚函数和虚函数,其中纯虚函数需要在虚函数后面加上=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


  1. http://www.gotw.ca/publications/mill18.htm ↩︎

  2. https://www.zhihu.com/question/431196480/answer/1587474895 ↩︎

  3. https://root.cern/TaligentDocs/TaligentOnline/DocumentRoot/1.0/Docs/books/WM/WM_132.html#:~:text=C%2B%2B%20has%20access%20control%2C%20but,from%20within%20the%20base%20class. ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值