• c++ 类内部函数调用虚函数


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

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

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

    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 0;
    }

    输出结果:

    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 0;
    }

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

    但是事实上,输出的是:

    i am A

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

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

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

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

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

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

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

  • 相关阅读:
    Vmware Vsphere WebService之vijava 开发一-vcenter连接、及集群信息获取
    Vmware Vsphere WebService SDK开发(第一讲)-基本知识学习
    RabbitMQ安装以及java使用(二)
    redis单机安装以及集群搭建(redis-6.2.6)
    Spring Cloud Gateway中Filter获取Request Body的几种方式
    电子发票插入微信卡包之PDF上传
    Elasticsearch集群搭建详解
    微服务的设计原则
    centos 7.4 64位 mysql的安装
    RabbitMQ安装以及java使用(一)
  • 原文地址:https://www.cnblogs.com/chenhuan001/p/7190079.html
Copyright © 2020-2023  润新知