1.什么是多态?
多态顾名思义,就是“多种形态”
在c++中,我们是通过虚函数来实现多态的
那么什么是虚函数呢?
虚函数就是在类的成员函数的前面加上virtual关键字,那么该成员函数就是虚函数
在c++中多态形成的条件是什么呢?
1.虚函数的重写;
2.父类的指针或引用调用重写的虚函数。
class A
{
public:
virtual void f1()
{
cout<<"A::f1()"<<endl;
}
};
class B : public A
{
public:
virtual void f1()
{
cout<<"B::f1()"<<endl;
}
};
void Test()
{
B b;//b是一个子类对象
A *p = &b;//此时形成了多态,这个时候调用函数与类型无关,只与指向对象有关,指向谁就调用谁
}
在这里我们进行了虚函数的重写,什么是虚函数的重写呢?
当子类定义了一个和父类完全相同的虚函数(返回值、函数名、可变参数列表)时,则称为重写。
特例:协变 A类虚函数的返回值可定义为A类的指针或引用 B类也是可以定义为B类的。(可以看做是切片类型)
//特例
class A
{
public:
A* fun()
{
}
};
class B : public A
{
public:
B* fun()
{
}
//此时也构成了重写
};
1. 单继承:一个类只有一个直接父类时称这个继承关系为单继承
2. 多继承:一个类有两个或以上直接父类时称这个继承关系为多继承
在这里需要了解一下虚表
虚表就是存了虚函数的表
虚函数能够实现多态的重要原因是有一个虚表指针指向了虚函数表,这样就可以通过指针来找到xuhans
单继承
//打印虚表
typedef void(*V_FUNC)(); //定义一个函数指针
void PrintVtable(int* vtable)
{
printf("vtable 0x%p\n",vtable);
int** ppvtable = (int**)vtable;
for(size_t i=0; ppvtable[i]!=0; i++)
{
printf("vatable[%u]::0x%p->",i,ppvtable[i]);
V_FUNC f = (V_FUNC)ppvtable[i];
f();
}
}
//单继承
class Base
{
public:
virtual void fun1()
{
cout<<"Base::fun1()"<<endl;
}
virtual void fun2()
{
cout<<"Base::fun2()"<<endl;
}
private:
int _a;
};
class Derive : public Base
{
public:
virtual void fun1()
{
cout<<"Derive::fun1()"<<endl;
}
virtual void fun3()
{
cout<<"Derive::fun1()"<<endl;
}
};
int main()
{
Base b;
Derive d;
PrintVtable(*((int**)&b));
PrintVtable(*((int**)&d));
return 0;
}
通过调试监视窗口,我们可以发现单继承的对象模型
多继承
//打印虚表
typedef void(*V_FUNC)(); //定义一个函数指针
void PrintVtable(int* vtable)
{
printf("vtable 0x%p\n",vtable);
int** ppvtable = (int**)vtable;
for(size_t i=0; ppvtable[i]!=0; i++)
{
printf("vatable[%u]::0x%p->",i,ppvtable[i]);
V_FUNC f = (V_FUNC)ppvtable[i];
f();
}
}
//多继承
class Base1
{
public:
void virtual Fun1()
{
cout<<"Base1::Fun1()"<<endl;
}
void virtual Fun2()
{
cout<<"Base1::Fun2()"<<endl;
}
private:
int _b;
};
class Base2
{
public:
void virtual Fun1()
{
cout<<"Base2::Fun1()"<<endl;
}
void virtual Fun3()
{
cout<<"Base2::Fun3()"<<endl;
}
private:
int _b;
};
class Derive:public Base1,public Base2
{
public:
void virtual Fun1()
{
cout<<"Derive::Fun1()"<<endl;//覆盖
}
void virtual Fun3()
{
cout<<"Derive::Fun3()"<<endl;
}
private:
int _d;
};
int main()
{
Base1 b1;
Base2 b2;
Derive d;
PrintVtable(*((int**)&b1));
PrintVtable(*((int**)&b2));
PrintVtable(*((int**)&d));
PrintVtable(*((int**)((char*)&d+sizeof(Base1))));
return 0;
}
3.菱形虚拟继承
//打印虚表
typedef void(*V_FUNC)(); //定义一个函数指针
void PrintVtable(int* vtable)
{
printf("vtable 0x%p\n",vtable);
int** ppvtable = (int**)vtable;
for(size_t i=0; ppvtable[i]!=0; i++)
{
printf("vatable[%u]::0x%p->",i,ppvtable[i]);
V_FUNC f = (V_FUNC)ppvtable[i];
f();
}
}
//菱形继承
class Base
{
public:
void virtual Fun1()
{
cout<<"Base::Fun1()"<<endl;
}
void virtual Fun2()
{
cout<<"Base::Fun2()"<<endl;
}
public:
int _b;
};
class Derive1:virtual public Base
{
public:
void virtual Fun1()
{
cout<<"Derive1::Fun1()"<<endl;
}
void virtual Fun3()
{
cout<<"Derive1::Fun3()"<<endl;
}
public:
int _d1;
};
class Derive2:virtual public Base
{
public:
void virtual Fun1()
{
cout<<"Derive2::Fun1()"<<endl;
}
void virtual Fun4()
{
cout<<"Derive2::Fun4()"<<endl;
}
public:
int _d2;
};
class Derive:public Derive1,public Derive2
{
public:
void virtual Fun1()
{
cout<<"Derive::Fun1()"<<endl;//覆盖
}
void virtual Fun5()
{
cout<<"Derive::Fun5()"<<endl;
}
public:
int _d;
};
int main()
{
Derive d;
d._b = 2;
d._d1 = 3;
d._d2 = 4;
d._d = 5;
PrintVtable(*((int**)&d));
return 0;
}
菱形虚拟继承的对象模型
在我们的菱形虚拟继承中,又有虚表也有虚基表,那么就需要我们在编译代码中看看内存中的状况,在图中我们可以看到,我们把基类的对象存为共有的,因此需要用虚基表用偏移量找到他。这里注意虚函数的重写,因为看的是Derive的对象模型,所以在继承的时候,有的函数进行了重写。