多态&虚函数&对象模型
多态
- 首先多态建立在继承的前提下,必须有虚函数的重写也称覆盖(父类必须为虚函数,子类可以不是)。
- 必须通过父类的指针/引用调用虚函数 指向谁就用谁的形态
- 多态与类型无关,只有对象有关。不同的对象调用相应的虚函数。
动态绑定:无法确定该函数是什么类型,只有在运行的时候,通过定义的对象所指的类型,才能确定要调用哪个类中的函数
class Person
{
public:
virtual void B()
{
cout << " 0 " << endl;
}
};
class Student : public Person
{
public:
virtual void B() //虚函数的重写(函数名 返回值 类型相同)——父类必须虚/子类可以不虚
{ //父类的指针/引用调用虚函数
cout << " 1 " << endl;
}
};
void Fun(Person& p)
{
p.B();//多态与类型无关,与对象有关
}
int main()
{
Person p;
Student s;
Fun(p);
Fun(s);
}
虚函数的使用规则
- 虚函数的重写要求返回类型,函数名,参数完全相同。
- 类外定义不用加virtual,只有声明是加。
- 构造函数不能是虚函数,最好不要把operator=定义为虚函数。
构造函数与析构函数内部不要调用虚函数,会发生未定义的行为。
构造函数->对象实例化->虚表指针->虚函数 在这个顺序中如果构造函数是虚函数,会是一个悖论。其次构造函数是对象自己调用的,不是通过父类的指针或引用,所以构造函数不能是虚函数
最好把父类的析构函数定义为虚函数。
基类的析构函数不为虚函数时,会造成内存泄漏,此时只会析构基类,不会调用派生类的析构函数析构派生类。因为虽然基类与派生类的函数名不一样,但编译器还是把他们看作函数的重写(协变)。当通过基类指针指向派生类是只会调用基类的析构函数(静态绑定)无法析构派生类。当定义为虚函数时,相当于多态,会调用派生类的析构函数去析构派生类(动态绑定)。
最后使用虚函数会有虚表指针,虚地址表,存取操作,所以使用虚函数是有成本,会降低内存与运行时间。
纯虚函数
在基类虚函数的后面加上”=0”,此时基类变成一个抽象类(接口类),抽象类不能实例化出对象,纯虚函数在派生类中重新定义后,派生类可以实例化出对象。所以不同的子类结果不同。
- 友元关系不能继承
- 静态成员可以继承,父子类共用同一个
静态成员/静态成员函数都存放着静态区,作用域为全局,所以共用。
虚表
虚函数在多态性中如此重要,他的存放方式也和普通的成员函数不同,虚函数存放在虚表中。
- 虚表是 用一段连续的内存空间来保存虚函数的地址,通过他能找到虚函数,相当于一个地图。
对象模型
- 探索单继承对象模型
class A {
public:
virtual void fun1() {}
virtual void fun2() {}
public: int _a;
};
class B : public A {
public:
virtual void fun1() {}
virtual void fun3() {}
public: int _b;
};
typedef void(*V_FUNC)(); //虚函数的类型重命名为V_FUNC
void PrintVTable(int* vtable)
{
int* vfArray = (int*)vtable;
printf("vtable:0x%p\n", vfArray);
for (size_t i = 0; vfArray[i] != 0; ++i)
{
printf("yfunc[%d]:0x%p->", i, vfArray[i]);
V_FUNC f = (V_FUNC)vfArray[i];
f();
}
cout << endl;
}
int main()
{
B b;
b._a = 1;
b._b = 2;
A a;
a._a = 3;
//PrintVTable((int*)*((int*)&b));
return 0;
}
B继承了A,对象b里包含了A类,b的虚函数存放在父类A的虚表中,A中的虚表指针指向虚表,虚表包含f1,f2,f3,内存布局如下
- 探索多继承对象模型
C继承了A和B,对象c中分别有A和B的俩个虚表,c的虚函数f4存放在A的虚表中,各类的数据成员分别存放在各类中,第一都为虚表指针(存放虚表的首地址)。
- 探索菱形虚继承
上篇文章中,我们探索了菱形继承,但没有在各类中加入虚函数,也就是在内存布局中没有存放虚表指针
可以看出于多继承不同的是将A类的部分放在了最后,他们共用一个A类,A B C 类都有虚表指针,B C 有虚基表指针,虚基表中存放俩个值,分别是虚基表指针到虚表指针的距离(-4),偏移量。