做项目的过程中,碰到一个问题。
问题可以抽象为下面的问题:
普通人吃饭拿筷子,小孩吃饭拿勺子。
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方法并进行调用。
原文链接: https://www.cnblogs.com/chenhuan001/p/7190079.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/256810
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!