C++三大特性:封装、继承、多态
在这里谈下多态,多态指的是在类之间存在继承关系时,有的函数声明为virtual函数,当我们将子类指针或引用转化为父类指针或引用时,调用某个虚函数时调用的是子类的虚函数。
编译器对于这种虚函数是在对象中存放了一个指针,这个指针指向一个表格,表格称之为虚函数表,在x86下,表格中的内容为多个连续的指针,这些指针指向一个个函数,当调用对象的虚函数时,会从这个表格中根据这个函数所在的顺序作为index从表格中取出指针,再调用这个函数。而index是在编译期即可确定的。
抽象模块成为一个类,再继承类的方式,然后在父类函数中有公共方法,在子类函数中有特定方法,在C++中经常用到。
还有一种方式:是虚基类,在抽象的父类中,不实现任何的方法,只保留接口,如下:
class A
{
public:
virtual void virtuFunc1()=0;
};
如下编写代码如 A a;因为类A是个虚函数接口,只能使用A *a 这种方式,因为此时类A是个抽象类接口,没有具体实现。
如上形式中我们不需要也无法实现类A中的函数virtuFunc1,而可以在子类中实现此方法。
class B : public A
{
public:
virtual void virtuFunc1()
{
printf("B\n");
}
};
如上,在B类中实现了virtuFunc1方法,需要注意的是如果在B类中不实现此方法,同样也无法编写B b这样的代码,当前已经编写了,所以可以编写B b的代码。
我们可以在不同的模块,或者在不同的类中抽象出一个抽象类接口,不实现任何的方法。这样可以将不同的模块组合在一起,使用同一个接口去处理代码,方便编写模块。
这些虚函数都是通过虚函数表来实现的。
后续还有V形继承,还有菱形继承。
V形继承如基类A,基类B,类C继承于A与B,如果类A与类B中都存在虚函数,那么类C中就存在两个虚函数表指针,这也是比较简单的结构,如:
class C : public A, public B
{
};
使用如下语句如C* c = new C; A* a = c; 编译器编译这条语句时,a变量值与c变量的值是一样的,直接进行转换,编译器生成的代码中,a变量直接赋值为c变量,如果使用这条语句 B* b = c;编译器编译这条语句时,会将c变量的值加一个偏移值再赋值给b,这个偏移值是编译器计算出来的,偏移值是类A所占的大小,这个大小包括类A所占的局部变量以及一个指向虚函数表的指针的大小,这个大小在32位程序中为4字节。
菱形继承比较复杂,还是按照上面的例子,如果A类与B类都继承自一个Base类,类中存在变量,那么当类D继承自A和B时,而A和B继承自Base类,这种情况如果画图那么就是菱形的,所以叫做菱形继承,出现这种情况,就需要使用虚继承,如下:
class C: virtual public A, virtual public B
{
};
如果不使用虚继承,那么当C继承A和B时,A、B又继承自Base,那么此时C中继承自Base中的变量就存在两份,编译器编译代码时会提示变量重复冲突,因为不知道C中的同名变量来自于A(继承自Base)还是来自于B(来自于Base),所以需要使用虚继承。
虚继承的实现方式是在对象内部使用了相对偏移来指向变量,这个后续补充。
标签:函数,继承,C++,编译器,Base,public,指针 From: https://www.cnblogs.com/ps12345678/p/16756583.html