C++回顾day03---

时间:2022-01-13 21:56:34

一:错误理解下的多态

#include <iostream>

using namespace std;

class Parent
{
public:
    Parent()
    {
        cout << "Parent construct" << endl;
    }

    void overrideFunc()
    {
        cout << "Parent override func" << endl;
    }
};

class Child:public Parent  //1.有继承
{
public:
    Child()
    {
        cout << "Child construct" << endl;
    }

    void overrideFunc()  //2.有函数重写
    {
        cout << "Child override func" << endl;
    }
};


void main()
{
    Parent* p = new Child();  //3.父类指针指向子类对象
    p->overrideFunc();      //4.进行多态调用
    system("pause");
}

C++回顾day03---

这里是根据指针类型来判断调用那个函数执行(这是不对的)--->虽然也叫作多态

二:错误讨论 :使用函数来讨论到底出错在哪里?

(一)再次回顾对象的产生(重点)(对于一个含有无参构造方法的类的对象的生成)

class Test
{
public:
    Test()
    {
        cout << "Test()" << endl;
    }

    Test(int a)
    {
        cout << "Test(int a)" << endl;
    }

    void getInfo()
    {
        cout << "use getinfo" << endl;
    }
};

void main()
{
   Test t; //会调用无参构造函数产生一个对象,若是不存在无参构造函数,则这里编译报错 Test t2(5); //会调用有参构造函数进行产生对象     
    t.getInfo();
    t2.getInfo();

    system("pause");
}

C++回顾day03---

重点:Test t3();是错误的!!!--->虽然声明不会报错,使用的时候会出错

C++回顾day03---C++回顾day03---

原因://warning C4930: “Test t3(void)”: 未调用原型函数(是否是有意用变量定义的?)
会认为该语句表示声明一个名为a的函数,返回类型是Test。

(二)详细错误案例指出《重点了解》

 

class Parent
{
public:
    Parent()
    {
        cout << "Parent construct" << endl;
    }

    void overrideFunc()
    {
        cout << "Parent override func" << endl;
    }
};

class Child01:public Parent
{
public:
    Child01()
    {
        cout << "Child01 construct" << endl;
    }

    void overrideFunc()
    {
        cout << "Child01 override func" << endl;
    }
};

class Child02 :public Parent
{
public:
    Child02()
    {
        cout << "Child02 construct" << endl;
    }

    void overrideFunc()
    {
        cout << "Child02 override func" << endl;
    }
};

void testMulStatus(Parent* p)  //想通过一个父类指针实现对子类对象方法的动态调用 { p->overrideFunc(); //想实现对象方法的动态调用 } void main()
{
    Child01 c1;
    Child02 c2;
    testMulStatus(&c1);
    testMulStatus(&c2);

    system("pause");
}
本意是通过同一个父类指针,实现对不同的子类的方法的调用

C++回顾day03---

三:真正的多态实现《重点》

 (一)多态原则

1.要有继承 2.要有virtual函数重写 3.要有父类指针指向子类对象

(二)代码测试

 

class Parent
{
public:
    Parent()
    {
        cout << "Parent construct" << endl;
    }

    virtual void overrideFunc()
    {
        cout << "Parent override func" << endl;
    }
};

class Child01:public Parent
{
public:
    Child01()
    {
        cout << "Child01 construct" << endl;
    }

    virtual void overrideFunc()
    {
        cout << "Child01 override func" << endl;
    }
};

class Child02 :public Parent
{
public:
    Child02()
    {
        cout << "Child02 construct" << endl;
    }

    virtual void overrideFunc()
    {
        cout << "Child02 override func" << endl;
    }
};

void testMulStatus(Parent* p)    //想通过一个父类指针实现对子类对象方法的动态调用
{
    p->overrideFunc();    //想实现对象方法的动态调用
}

void main()
{
    Child01 c1;
    Child02 c2;
    testMulStatus(&c1);
    testMulStatus(&c2);

    system("pause");
}

C++回顾day03---

四:原理探析(虚函数表指针VPtr)

(一)多态中的虚函数表

当类中声明虚函数时,编译器就会在类中生成一个虚函数表 虚函数表是一个存储类成员虚函数指针的数据结构 虚函数表由编译器自动生成和维护 virtual成员函数会被编译器放入虚函数表中 存在虚函数时,每个类中都会有一个指向虚函数表的指针(vptr指针)
注意:虽然定义为虚函数,但是占用空间和原来成员函数大小是一样的,所以在内存上面唯一的区别在于多了一个虚函数指针(大小4字节)

(二)证明虚函数指针的存在

class Test
{
public:
    int a;
    int b;
public:
    Test()
    {

    }

    void getInfo()
    {
        cout << "getInfo" << endl;
    }

    void setInfo()
    {
        cout << "setInfo" << endl;
    }
};

class Test02
{
public:
    int a;
    int b;
public:
    Test02()
    {

    }

    virtual void getInfo()
    {
        cout << "getInfo" << endl;
    }

    virtual void setInfo()
    {
        cout << "setInfo" << endl;
    }
};

void main()
{
    cout << sizeof(Test) << endl;
    cout << sizeof(Test02) << endl;

    system("pause");
}

C++回顾day03---

无论是一个虚函数,还是两个或者多个虚函数,最终内存大小相差4字节。所以一个成员函数--->虚成员函数是不增加空间大小的。多的那4字节是由于虚函数表指针的增加导致的

(三)多态上虚函数调用步骤

每个对象会含有一个虚函数指针vptr,指向虚函数表,当父类指针调用重写虚函数时:首先会去找vptr,根据vptr去找对应的子类虚函数表,从而找到对应对象的方法进行调用

(四)虚函数和普通函数调用的区别

1.若是普通成员函数,编译器可以直接确定被调用成员函数(静态编链)
2.若是虚函数:编译器要根据对象的vptr指针,找到虚函数表,找到对应的虚函数进行调用(动态编链)
3.所以虚函数的调用效率要低于普通成员函数,所以出于效率,没有必要全部定义为虚函数