做项目的过程中,碰到一个问题。

问题可以抽象为下面的问题:

普通人吃饭拿筷子,小孩吃饭拿勺子。

class People {
public:
void eat() {
get_util_to_eat();
} virtual void get_util_to_eat() {
std::cout << "People get chopsticks" << std::endl;
}
}; class Children : public People {
public:
void get_util_to_eat() {
std::cout << "Children get scoop" << std::endl;
}
}; int main() {
People* people = new Children();
people->eat();
return ;
}

输出结果:

Children get scoop

当然这也符合我们的预期。

因为people不是虚函数,所以上述程序调用的是people中的eat方法,这就涉及到一个之前我一直模糊的概念,在一个类方法中调用虚方法,是如何调用的。

这又涉及到之前不得不说的一个问题:

class A {
public:
void print() {
std::cout << "i am A" << std::endl;
}
}; int main() {
A* a = NULL;
a->print();
return ;
}

上述代码会输出什么,按照直观的感觉NULL怎么可能调用方法呢,要出core吧。

但是事实上,输出的是:

i am A

调用类函数的时候,c++编译器并不会管该类是否为空,而是将该类的地址当做this指针传到函数中去。

a->print() 时,在编译器中就相当于print(a)

有因为print中没有用到成员变量的情况,所以自然能很正确的运行。

然后来看下汇编代码就能更理解了。以下是People类内的汇编代码。

        void eat() {
0x0000000000400bd2 <+>: push %rbp
0x0000000000400bd3 <+>: mov %rsp,%rbp
0x0000000000400bd6 <+>: sub $0x10,%rsp
0x0000000000400bda <+>: mov %rdi,-0x8(%rbp) //rsp表示第一个参数,也就是类的指针
get_util_to_eat();
0x0000000000400be9 <+>: mov -0x8(%rbp),%rax //将类指针放入rax寄存器中
0x0000000000400bed <+>: mov (%rax),%rax //取首地址值,也就是虚表地址
0x0000000000400bf0 <+>: mov -0x8(%rbp),%rdi //放入rdi中,下次函数调用的时候取参用
0x0000000000400bf4 <+>: mov (%rax),%rax //取出虚表中函数的地址
0x0000000000400bf7 <+>: callq *%rax //调用改函数

总结就是,进入类的非静态成员函数时,会默认携带类的指针(this),然后改函数内用到成员变量、成员方法都等同于在前面加了一个this->

So 回到最初的那个问题,在People::eat中传入的是Chilren的指针,所以调用 get_util_to_eat 时从虚表中取出了Children::get_util_to_eat方法并进行调用。

c++ 类内部函数调用虚函数的更多相关文章

  1. C++构造函数和析构函数调用虚函数时都不会使用动态联编

    先看一个例子: #include <iostream> using namespace std; class A{ public: A() { show(); } virtual void ...

  2. C++学习笔记(十二):类继承、虚函数、纯虚函数、抽象类和嵌套类

    类继承 在C++类继承中,一个派生类可以从一个基类派生,也可以从多个基类派生. 从一个基类派生的继承称为单继承:从多个基类派生的继承称为多继承. //单继承的定义 class B:public A { ...

  3. C++基础知识 基类指针、虚函数、多态性、纯虚函数、虚析构

    一.基类指针.派生类指针 父类指针可以new一个子类对象 二.虚函数 有没有一个解决方法,使我们只定义一个对象指针,就可以调用父类,以及各个子类的同名函数? 有解决方案,这个对象指针必须是一个父类类型 ...

  4. 自绘CListCtrl类,重载虚函数DrawItem

    //自绘CListCtrl类,重载虚函数DrawItem void CNewListCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { // TOD ...

  5. 继承虚函数浅谈 c++ 类,继承类,有虚函数的类,虚拟继承的类的内存布局,使用vs2010打印布局结果。

    本文笔者在青岛逛街的时候突然想到的...最近就有想写几篇关于继承虚函数的笔记,所以回家到之后就奋笔疾书的写出来发布了 应用sizeof函数求类巨细这个问题在很多面试,口试题中很轻易考,而涉及到类的时候 ...

  6. C++:抽象基类和纯虚函数的理解

    转载地址:http://blog.csdn.net/acs713/article/details/7352440 抽象类是一种特殊的类,它是为了抽象和设计的目的为建立的,它处于继承层次结构的较上层. ...

  7. (转)(C++)关于抽象基类和纯虚函数

    ★抽象类:一个类可以抽象出不同的对象来表达一个抽象的概念和通用的接口,这个类不能实例化(创造)对象. ★纯虚函数(pure virtual):在本类里不能有实现(描述功能),实现需要在子类中实现.例: ...

  8. C++入门经典-例8.9-抽象类,纯虚函数,创建纯虚函数

    1:包含有纯虚函数的类称为抽象类,一个抽象类至少具有一个纯虚函数.抽象类只能作为基类派生出的新的子类,而不能在程序中被实例化(即不能说明抽象类的对象),但是可以使用指向抽象类的指针.在程序开发过程中并 ...

  9. C++ 构造函数或析构函数调用虚函数

    构造函数和析构函数中的虚函数 在执行基类构造函数时,对象的派生类部分是未初始化的.实际上,此时对象还不是一个派生类对象. 为 了适应这种不完整,编译器将对象的类型视为在构造或析构期间发生了变化.在基类 ...

随机推荐

  1. DOS批处理基础

    1.  echo 和 @ 回显命令 @                   #表示不显示@后面的命令 echo off               #从下一行开始关闭回显 @echo off      ...

  2. 【转】Spring Boot干货系列:常用属性汇总

    转自Spring Boot干货系列:常用属性汇总 附录A.常用应用程序属性 摘自:http://docs.spring.io/spring-boot/docs/current/reference/ht ...

  3. 谈谈Android中的Rect类——奇葩的思维

    最近在工作中遇到了一些问题,总结下来就是Android中Rect这个类造成的.不得不说,不知道Android SDK的开发人员是怎么想的, 这个类设计的太奇葩了.首先介绍一下Rect类:Rect类主要 ...

  4. C#实现http协议支持上传下载文件的GET、POST请求

    C#实现http协议支持上传下载文件的GET.POST请求using System; using System.Collections.Generic; using System.Text; usin ...

  5. Thinkphp5模板继承

    代码 application\index\controller\index.php <?php namespace app\index\controller; use app\index\con ...

  6. DWZ主从表界面唯一性验证(自写js)(二)

    上篇介绍了自写js判断的前三项,本篇博客介绍第四步,关于触发课程代码文本框的离开事件后,判断一整列的课程代码之间是否有重复的值. 此问题可以提取为判断一个数组里是否有重复值,重复值是什么. 第四步→判 ...

  7. 将Unity导出的Eclipse工程转换为AndroidStudio工程

    步骤:1)将unity项目导出到文件夹: 转换到安卓平台,这里只勾选google android project.然后导出到自己新建的文件夹. 2)打开导出的文件夹,看到如下内容.这是unity5.x ...

  8. 本地存储(LocalStorage、SessionStorage、Web SQL Database、Indexed DB)

    https://www.cnblogs.com/SeeYouBug/p/6127001.html https://blog.csdn.net/inter_peng/article/details/49 ...

  9. MapWinGIS------下载与安装

    最新版本下载地址: https://github.com/MapWindow/MapWinGIS/releases 1.下载后按步骤安装即可 2.右键以管理员身份运行cmd,运行:regsvr32 C ...

  10. C#------如何处理缺少对公共可见类型或成员的xml注释的警告

    出现警告的原因: 使用Swagger框架时 如图,只要加上注释就可以了 使用前: 使用后: