函数调用的本质:
- 函数调用实际上是执行函数体中的代码
- 函数体是内存中的一个代码段
- 函数名代表改代码段的首地址,函数执行时就从这里开始
所以执行一个函数的时候,需要知道具体的函数地址,才能执行函数
重载、重写和隐藏的区别
(1)重载(静态联编)指的是同一个名字的函数,必须具有不同的参数列表(参数类型、个数),可以有不同的返回类型,根据参数列表和返回类型决定调用哪一个函数;(函数名可以相同,但是参数不能完全相同,至于返回值,不影响重载。所以函数倾轧不使用返回值信息)
(2)重写(覆盖,动态联编)指的是,派生类中的函数重写了基类中的虚函数,重写的基类的中函数必须被声明为virtual,并且返回值,参数列表和基类中的函数一致;
(3)隐藏是指,派生类中的同名函数把基类中的同名函数隐藏了,即基类同名函数被屏蔽掉;此时基类函数不能声明为virtual。
实际上在c++中,无论重载还是重写,最终都会生成多个不同的函数,这些函数虽然在代码上看起来名字相同,实际上是名字不同的多个函数,对于重载,c++使用函数倾轧技术,在ide看来,重载的不同函数的参数和返回值都会使得经过函数倾轧后表现的不同,重写更不必说,不同虚函数表指向的函数显然不同。
对于重写,其函数名,参数列表,返回值类型,所有都必须同基类中被重写的函数一致,所以在代码编写过程中,我们的调用代码完全相同:
#include <iostream>
using namespace std;
class A {
public:
A() { cout << "A" << endl; }
virtual void AB_test() { cout << "A_test" << endl; }
//~A() { cout << "~A" << endl; AB_test(); }//case 3
virtual ~A() { cout << "~A" << endl; AB_test(); }//case 4
};
class B :public A {
public:
B() { cout << "B" << endl; }
~B() { cout << "~B" << endl; AB_test(); }
virtual void AB_test() { cout << "B_test" << endl; }
};
int main()
{
A* myObject= new A;
A* temp = myObject;
myObject->AB_test();
myObject = new B;
myObject->AB_test();
cout << "Than delete" <<endl;
delete temp;
delete myObject;
return 0;
}
执行结果:
关于其中构造和析构调用看不懂的可以看我的这篇文章:
C++继承机制下析构和构造函数的执行分析
可以看到,myObject->AB_test()的两次调用代码完全相同,那如果这两次调用的时候要在运行期间读取文本信息readRawData()来确定myObject->AB_test()到底执行基类还是子类中的函数,这将使用完全相同的代码,但行为却要在运行期才能确定,所以重写自然地成为动态联编,行为确定在运行期。
重载则完全不同,它具有不同的形参列表,也就睡说实际上在编写代码的时候,就已经确定了行为,因为你需要指定参数来确定调用哪一个重载的函数,自然其行为在编译期已经确定,是静态行为。
关于函数倾轧,简单的说就是根据函数名和参数列表将参与重载的函数实际上又赋予了不同的名字,例如:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zXZSzXQq-1687857233730)(C:\Users\21131\AppData\Roaming\Typora\typora-user-images\image-20230627171032672.png)]
上图来自这篇博客,引用一下供大家参考,具体请见大佬博客:
C++函数重载及原理(命名倾轧name mangling)小记