C++中的虚函数

C++中的虚函数

引言

C++中的虚函数,是实现C++多态的一个重要手段。这里会介绍一下虚函数和RTTI。


一、虚函数表和虚函数表的指针

要了解C++是如何实现虚函数这功能,首先我们就要了解一个重要概念:虚函数表(Virtual Tables,之后简称vtbls)。

虚函数表是类额外的一个静态数组。其中存放该类的虚函数信息等。

在编译的时候,编译器同时也会为基类创建一个成员变量:用于存放类对象的虚拟表的位置(virtual Table Pointers,之后简称vptrs)。

下面举一个例子来解释虚函数表。

class CBase
{
public:
    virtual void f1();
    virtual void f2(int i);
    void f3(const char * p);
};
class CSub1 : publicCBase
{
public:
    virtual void f1();
};
class CSub2 : publicCBase
{
public:
    virtual void f2(int i);
};
 
int _tmain(int argc,_TCHAR* argv[])
{
    CBase * p1 = newCSub1();
    p1->f1();
    p1->f2(1);
    delete p1;
 
    CBase * p2 = newCSub2();
    p2->f1();
    p2->f2(1);
    delete p2;
 
    return 0;
}
下图便是其中虚函数表的具体情况
图1.1

在程序中,new CSub1(),系统为这个new出来的对象的创建一个数组,存放了CSub1对象的虚函数信息,并且将这个数组的地址赋值给该对象的vptrs

接下来将CSub1对象的地址付给了指针p1。而这个指针的类型是CBase*,但是它所指向的对象却是CSub1

当执行语句p1->f1()的时候,利用指针找到内存中的CSub1对象,随后再通过该对象的vptrs找到CSub1对象的虚函数表,在表中找到了虚函数f1实现的地址,找到真正的f1函数。可以看图1.1,CSub1的f1所指向的是CSub1自己的类成员函数f1。

当执行语句p1->f2(1)的时候,和上面的步骤一样,最终找到了CBasef2

同样p2指针也类似,这里就不做累述。


二、RTTI

Run-Time Type Information,在程序运行时,能够判断一个对象的类型。承接上一节提到的静态类型和动态类型。

void deal(CBase * p);
……
deal(new CSub1());

这里作为函数deal的参数p的静态类型是CBase*,这是在编译器进行编译的时候,能够判断的类型。但是,如果我们将CBase的子类CSub1的对象的指针作为参数给p赋值后,p的动态类型便是CSub1。所谓动态类型便是在程序运行的时候,对象实际的类型。这就是RTTI要做得事。

 

对于RTTI,其中介绍两个个操作。

(1)dynamic_cast,用于多态类型的转换。经常被用来将基类对象的指针转换成子类对象的指针。

比如:

CBase * p = newCSub1();
CSub1 * ps1 = p;//1
CSub1 * ps1 =dynamic_cast<CSub1 *>(p); //2

上面第一种转换,编译器是无法通过的,但是第二种转换编译是没有问题的。

 

(2)typeid,用于确定对象的精确类型。记住这里是对象的类型,传入一个指针,它只会去判断这个指针的类型,而不是这个指针指向对象的类型。

CBase * p1 = newCSub1();
CBase * p2 = newCSub2();
CBase * p = newCBase();
cout<<"typeid(p).name():"<<typeid(p).name()<<endl;
cout<<"typeid(p1).name():"<<typeid(p1).name()<<endl;
cout<<"typeid(p2).name():"<<typeid(p2).name()<<endl;
cout<<"typeid(*p).name():"<<typeid(*p).name()<<endl;
cout<<"typeid(*p1).name():"<<typeid(*p1).name()<<endl;
cout<<"typeid(*p2).name():"<<typeid(*p2).name()<<endl;
delete p1;
delete p2;
delete p;

这里的输出便是:


其实typeid这个操作返回的类型是type_info(这里就不再做介绍了)。


三、总结

在C++中利用虚函数,可以实现很多面向对象的模式。可以在设计模式(Design Pattern)中的各处找到虚函数的影子,所以要怎么用好虚函数,可以去参考设计模式。

 

Java企业场景下的实战入门课(Spring Boot+Redis)

04-20
<p> 企业场景下的Java实战课程!  </p> <p> 【超实用课程内容】 本课程主要是从最基础的技术要点一步一个脚印的介绍Spring Boot2.0相关的核心技术栈和缓存中间件Redis常见且典型的数据结构、相关的核心技术栈及典型的应用场景的实战。并附带业务场景实战用户注册和点赞系统中点赞功能模块的设计与实现为各位小伙伴提供企业级项目开发中常见且典型的Java核心技术,可以说是拒绝纸上谈兵、注重实战并学以致用!  </p> <p> <br> </p> <p> 套餐中一共包含2门实战入门课程(共82讲)  </p> <p> 课程1:《Java实战之Spring Boot入门到精通》  </p> <p> 课程2:《Java实战之Redis入门到精通》  </p> <p> <br> </p> <p> 【基础要求】  </p> <p> 1、基本要求:具备一定的JavaSE以及Java Web项目的开发基础、了解spring boot更佳  </p> <p> 2、工具要求:会使用Intellij IDEA、Navicat以及Postman  </p> <p> <br> </p> <p> 【你能收获到什么?】  </p> <p> 1、帮助学员了解并掌握springboot和缓存中间件Redis的方方面面、包括其典型及常用的数据结构及其在实际项目开发中典型的应用场景!  </p> <p> 2、掌握如何基于Spring Boot搭建企业级项目,整合加入中间件Redis相关的依赖配置,并以此为扩展,为后续学习其他中间件做铺垫;可以提升学员Java中间件的实战能力。  </p> <p> 3、帮助学员了解并掌握缓存中间件Redis在实际应用中有哪些常见、典型的应用场景,如对象信息存储、列表存储、队列特性分发消息、试题库随机获取、排行榜等等,这对于学员在平时项目开发、跳槽面试等情况下将有很大的帮助  </p> <p> 4、本课程介绍的基于Redis相关数据结构的特性独立设计并实战项目中典型功能模块,如会员到期自动提醒、点赞功能模块等内容,将有助于学员将所学的技术栈真正应用到实际中、提升自身的数据库设计能力、业务理解能力、代码实战能力以及性能优化方面的能力  </p> <p> <br> </p> <p> 【课程如何观看?】  </p> <p> 1、登录CSDN学院 APP 在我的课程中进行学习;  </p> <p> 2、移动端:CSDN 学院APP(注意不是CSDN APP哦) 本课程为录播课,课程2年有效观看时长 【资料开放】 课件、课程案例代码完全开放给你,你可以根据所学知识,自行修改、优化 下载方式:电脑登录课程观看页面,点击右下方课程资料、代码、课件等打包下载 </p> <p> <img src="https://img-bss.csdn.net/202004200821078434.png" alt=""> </p>
©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值