前面两篇C++57个入门知识点_35 函数覆盖的概念1(函数覆盖条件:父子类继承关系、函数名&参数列表&返回值&调用约定必须相同、有virtual关键字;函数覆盖:类虚表中成员函数从继承自父类变为自己的),C++57个入门知识点_36 函数覆盖的概念2( 虚表大小由编译器在编译时确定;虚表中虚函数的顺序:父类虚函数顺序决定了子类虚函数的顺序,子类自己的虚函数会出现在自己虚表中父类对应所定义的虚函数后面)介绍了函数覆盖的基本概念,本篇介绍虚函数的直接调用与间接调用。
总结:
- 直接调用:根据函数名称,直接调用该函数(编译器在编译时候就确定了)
(1)普通的函数调用
(2)对象的普通成员函数的调用
(3)对象的虚函数调用
- 间接调用(虚调用,即通过查虚表来调用函数):虚函数,通过查找对象的虚表下标来调用函数的方法(在运行时期确定调用谁)
(1)通过对象的指针调用虚函数
(2)通过对象的引用调用虚函数
C++中对函数的调用分为直接调用和间接调用,间接调用是虚函数所具有的的性质。
1. 直接调用
根据函数名称,直接调用该函数(编译器在编译时候就确定了)
(1)普通的函数调用
(2)对象的普通成员函数的调用
(3)对象的虚函数调用
#include <iostream>
void foo() {
printf("foo");
}
int main(int argc, char* argv[])
{
foo();
return 0;
}
运行结果:main
函数中写入foo();
就相当于将foo();
的函数地址写入该位置,在foo();
位置设置断点可以看到程序运行时直接调用
#include <iostream>
class CChinese
{
public:
virtual void foo() {
printf("CChinese Foo!");
}
};
int main(int argc, char* argv[])
{
CChinese chs;
chs.foo();
return 0;
}
运行结果:直接调用类的虚成员函数,这是因为虽然其为虚函数,但是在调用时已经明确指出了对象和调用的函数,因此是直接调用。
2. 间接调用(虚调用)即通过查虚表来调用函数
虚函数,通过查找对象的虚表下标来调用函数的方法(在运行时期确定调用谁)
- 通过对象的指针调用虚函数
- 通过对象的引用调用虚函数
2.1 通过对象的指针调用虚函数
#include <iostream>
class CChinese:public CPerson
{
public:
virtual void fly() {
printf("Cperson Fly!");
}
virtual void eat() {
printf("Cperson Eat!");
}
virtual void speak() {
printf("CChinese Speak!");
}
virtual void joke() {
printf("CChinese Joke!");
}
virtual void foo() {
printf("CChinese Foo!");
}
};
int main(int argc, char* argv[])
{
CChinese* pChs = &chs;
pChs->foo();
return 0;
}
运行结果:调用的过程–>查虚表–>虚表下标–>函数
2.2 通过对象的引用调用虚函数
产生的效果是与通过对象的指针调用虚函数一致的,均为间接引用
int main(int argc, char* argv[])
{
CChinese& chsRef = chs;
chsRef.foo();
return 0;
}
运行结果:
3. 一种使用了虚函数及指针的直接引用
明确了类域范围,所以不论是否是指针或引用,均是直接调用
int main(int argc, char* argv[])
{
CChinese* pChs = &chs;
pChs->CChinese::foo();
return 0;
}
4.学习视频地址:C++57个入门知识点_37 虚函数的直接调用与间接调用