多态原理

来源:互联网 发布:咸鱼淘宝二手网 编辑:程序博客网 时间:2024/06/07 02:20

1、多态原理的实现
我们知道多态实现的三个条件:1.继承,2.虚函数重写,3.父类指针或引用指向子类对象
当类中有一个函数是虚函数,会在类中增加一个 虚函数指针,虚函数指针会指向一个虚函数表; 虚函数表是一个存储类成员函数指针的数据结构; 虚函数表是由编译器自动生成与维护的; virtual成员函数会被编译器放入虚函数表中; 存在虚函数时,每个对象中都有一个指向虚函数表的指针(vptr)

void func(){}virtual void func ()  // print (A * const this)调用:p->func();  // p->vfptr->func()

调用虚函数过程
最后在调用的时候,编译器就会根据func是否为虚函数来进行相应的解释: 1.如果不是虚函数:编译器可直接确定被调用的成员函数(也叫做静态联编) 2.如果是虚函数:编译器根据对象p的VPTR指针所指向的虚函数表中来查找func函数并调用(也叫做动态联编或者迟邦定),也可以说是通过该指针去取 这块地址上的 虚函数指针,通过虚函数指针 去 虚函数表中 去取同名函数
注意:通过虚函数表指针VPTR调用重写函数是在程序运行时进行的,因此需要通过寻址操作才能确定真正应该调用的函数,而普通成员函数是在编译器时就确定了调用谁,因此虚函数的效率要低很多,所以不是虚函数越多越好。C++编译器,执行run函数,不需要区分是子类对象还是父类对象

2、构造函数中调用虚函数能否实现
构造的顺序是先构造父类、再构造子类
当调用父类的构造函数的时候,虚函数指针vfptr 指向父类的虚函数表
当父类构造完,调用子类的构造函数的时候,虚函数指针 vfptr 指向子类的虚函数表
结论:构造函数中无法实现多态

class A{public:    A()    {        // 在构造函数中调用虚函数是不能实现多态的        // 虚函数指针 是分步初始化,一开始指向父类的虚函数表,父类初始化完了以后 指向自己的虚函数表        print();    }    virtual void print()    {        printf ("AAAAAAAAAAAAAAAAAAAAA\n");    }public:    int a;};

3、不要用父类指针指向子类数组
指针也是一种数据类型,C++类对象的指针p++/–,仍然可用。
指针运算是按照指针所指的类型进行的。
父类p++与子类p++步长不同;不要混搭,不要用父类指针++方式操作子类对象数组
下面这个案例可以看出:

class A{public:    A(int a)    {        this->a = a;    }    virtual void print()    {        printf ("a = %d\n", a);    }public:    int a;};class B:public A{public:    B(int a, int b): A(a)    {        this->b = b;    }    virtual void print()    {        printf ("a = %d, b = %d\n",a, b);    }public:    int b;};int main(){    B b[5] = {B(1,2), B(3,4), B(5,6), B(7,8), B(9,10)};    B *pb = b;    A *pa = b;    // 一般来讲  基类 和 派生类 的指针步长是不一样的, 除非派生类没有增加任何成员    // pa + 1 =  pa + sizeof(A)   pb + 1 = pb + sizeof(B)    // 不要基类的指针 操作派生类的数组    for (int i = 0; i < 5; i++)    {        // pb[i].print();        // pa[i] ==> pa + i ==> (int)pa + sizeof(A) ==> (int)pa + 8        // sizeof(B)  =  12        pa[i].print();         // long * p = 0x100000  ==> p + 1 = 0x10004  ==>  (long)p + 1 = 0x10001    }    return 0;}