c++多态原理

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

using namespace std;

class Parent
{
public:
    //要避免在构造函数中进行业务处理,如果在构造函数中进行业务处理,会产生很多未知错误,构造函数一般只用来给类成员变量进行初始化就可以了
    //一个类中有虚函数,那么在编译器给这个对象开辟空间的时候,会默认增加一个虚函数表指针(vptr),永远指向这个类的虚函数表
    virtual void print()//函数前有virtual关键字时,不论会不会被继承,都会创建一个虚函数表,每个类都专属有一个虚函数表,这个虚函数表中存储这个类中所有虚函数的地址
    {
        cout << "parent......" << endl;
    }
    int a;
};

//虚函数表指针的分步初始化:
//构建子类对象时,会先构建父类的对象,也会构建父类对象的虚函数表,此时子类的虚函数表指针会临时指向父类的虚函数表
//再构建子类额外的变量,此时会调用子类的构造函数,也会构建一个子类的虚函数表,此时子类的虚函数表指针由父类的虚函数表指向子类自己的虚函数表,因此子类对象的虚函数指针是分步初始化的
class Child :public Parent
{
public:
    virtual void print()
    {
        cout << "child......" << endl;
    }
};

void func(Parent &p)
{
    //当父类不为虚函数,子类为虚函数时,编译器根本不会查找虚函数表,即使传进来的是子类对象的引用,但编译器会先判断这个函数在父类对象中是否为虚函数,不为虚函数则根本不查找虚函数表,和子类中是不是虚函数没有任何关系了
    //虚函数指针调用重写虚函数是在程序运行时进行的,因此虚函数效率要低很多,出于效率考虑,没有必要把所有成员函数都声明为虚函数
    p.print();//当对象调用函数时,编译器会先通过虚函数表指针去查询虚函数表中是否有满足的虚函数,如果有,先执行虚函数表中的虚函数
}

class A
{
public:
    A(int a)
    {
        this->a = a;
    }
    virtual void print()
    {
        cout << this->a << endl;
    }

    int a;
};

//子类指针和父类指针的步长是不一定相等的,需要看子类中有没有新定义成员变量,下面这个子类就新定义了一个成员变量,因此子类指针和父类指针的补偿是不一样的
class B :public A
{
public:
    B(int a) :A(a)
    {
        this->b = 10;
    }
    virtual void print()
    {
        cout << this->a << endl;
    }

    int b;
};

int main()
{
    Child c;
    func(c);
    cout << "sizeof(parent)=" << sizeof(Parent) << endl;//这个值为8,因为多了一个虚函数表指针

    //下面两个代码即发生了多态,并不一样要传参进入全局函数中发生多态
    Parent *p = new Child;
    p->print();//这儿发生了多态,调用的是子类里的print()函数
    delete p;

    B array[] = { B(0), B(1), B(2) };//这为类型b的数组,数组里的对象可以用构造函数直接构造,并不是匿名对象,array[0]=B[0]...等
    A *a = &array[0];//父类指针指向子类对象,即用子类地址给父类指针赋值
    B *b = &array[0];//这就是正常的赋值,子类指针指向子类对象
    a->print();//发生多态
    b->print();//正常调用

    //a++;这行代码是错误的,因为这本质是还是父类型的指针,它的步长比子类指针步长要小,这种++不会完全跳过一个子类对象的完整地址,这时候再用这个指针去调用后一个对象的方法或变量时,地址是错误的
    //因此当子类型的长度比父类型长度长时,在用父类型指针指向子类型数组时,一定不要用++,因为父类指针步长小于子类指针步长,父类指针++后所在的位置是个错误位置
    b++;//这是可以的,子类指针步长即为一个完整的子类对象地址
    return 0;
}

猜你喜欢

转载自blog.csdn.net/tulipless/article/details/80765598