浅析C++中的虚函数(一)
在C++中虚函数是很重要的一个知识点,对于它,还是感觉似懂非懂,这次写下来当作总结了吧。
虚函数的思想是,根据指向或引用的对象类型调用虚函数,而不是根据指针本身或引用本身的类型,虚拟函数在运行时后期解析。换句话说,存在虚函数的类创建一个指向子类的指针或引用,当该指针指向子类时,可以调用派生类中重载父类的方法,没必要管派生类对象是什么,引用也是如此。具体的代码如***释说的很清楚了:
#include <iostream>
using namespace std;
class A
{
public:
virtual void f()
{
cout << "这是 A" << endl;
}
};
class B :public A
{
public:
void f()
{
cout << "这是 B" << endl;
}
void f1()
{
cout << "okokok" << endl;
}
};
int main()
{
/*
这是引用的方式
B b;
A &a = b;
a.f();
*/
B *b = new B();
A *a = b;
a = b; //B类型的指针赋值给A类型的指针,a指向类B
a->f();
//这里只能调用被重写的f(),想要调用f1()需要用到其他方法
system("pause");
return 0;
}
虚函数是如何做到这点的,我从其他地方找到一个比较好的解释,编译器维护了两个表,vptr和vtable,其中vptr是用来指向每个不同类中vtable,每个类只有一个vtable,在每个vtable中有虚函数的地址,vptr是针对每个对象来说的,每个对象都会有一个vptr,所以,只有虚函数没有其他的类的对象大小为4(32位),一般是在对象的地址的开头,和第一个变量之间空出来的空间就是这个指针vptr,当基类对象的vptr被子类对象的vptr赋值时,基类对象的指针就可以指向子类的vtable,这样就实现了动态绑定(或者成为后期绑定或运行时的动态)。如图所示,感觉越说越不明白……
因为通过vptr指向每个类中的虚函数,所以虚函数是不能为static的,static类型的不属于任何一个类。同时因为虚函数是在后期绑定的,当然不能成为内联函数,因为内联函数在编译时期就已经插入到调入点了。
虚函数除此之外还有一些其他有意思的现象,虚函数中的默认参数问题,上代码。
#include <iostream>
using namespace std;
class A
{
public:
int a = 1;
int b = 2;
virtual void f(int x = 0)
{
cout << "这是 A 的 x=" << x << endl;
}
};
class B :public A
{
public:
void f(int x)
{
cout << "这是 B 的 x=" << x << endl;
}
};
int main()
{
B *b = new B();
A *a = b;
a->f();
system("pause");
return 0;
}
这里的运行结果是
类B中的虚函数参数使用了类A中的默认值,因为默认参数不参与函数签名(函数签名由参数个数与其类型构成)。因此,基类和派生类中f()的签名被认为是相同的,因此覆盖了基类的f()。此外,默认值在编译时使用。当编译器发现函数调用中缺少参数时,它会替换给定的默认值。因此,在上面的程序中,x的值在编译时被替换,并且在运行时调用派生类的f()。因为a是A类型的指针,即使类B中的x存在默认值,也会在编译时被基类的替换。因为虚函数中存在这些套路,所以一般在虚函数中不使用默认值。
C++中虚函数也可以是private的,这个我之前也没有仔细看过想过,也不知道这个性质有什么特别的用处,总之先了解一下吧。比如:
#include <iostream>
using namespace std;
class A
{
private:
int a = 1;
int b = 2;
void f()
{
cout << "这是 A" << endl;
}
friend int main();
};
class B :public A
{
public:
void f()
{
cout << "这是 B" << endl;
}
};
int main()
{
B *b = new B();
A *a = b;
b->f(0);
system("pause");
return 0;
}
因为main函数是A类的朋友,所以可以访问,如果不是,编译就不会通过,这个不算是解释,但现在先不急着追究了。
以上的东西大部分都是总结网上看到的,书上写的结构全,但总感觉有些细节的地方没说,网上的全倒是全,就是零零散散的,把我知道的总结一下吧。
这只是虚函数的一小部分,还有没写完的,接着写写。