面向对象的特性---多态

  • 多态

        1.虚函数:在类的成员函数前加virtual即构成虚函数。作用是通过基类的指针与引用调用派生类的成员函数

        2.多态的概念

        多态即多种形态,通过调用不同的函数实现不同的功能。

        如买票机制,不同的对象买票的制度也不同,如普通人买全票,学生可买半价票等。

        3.多态的条件

        (1)虚函数的重写(子类定义一个与父类的完全相同的虚函数)

        (2)父类的指针或引用

                当使用父类的指针或引用调用这个重写的虚函数时,指针指向父类就调用父类的虚函数,指向子类就调用子类的虚函数。

 面向对象的特性---多态               

            注意

                    (1)基类(父类)必须为虚函数,派生类(子类)保持这一特性。(相当于父类必须为虚函数,子类可为虚函数也可不为虚函数)

                       (2)除外,派生类(子类)的重写必须保持函数名、参数、返回值与基类(父类)保持相同。

                       (3)协变就是派生类的中函数的返回值可以与基类的不同,但返回值必须是指向父子关系的指针或引用。

                        面向对象的特性---多态

                  (4)静态成员函数不能为虚函数。因为静态成员函数就相当于是受命名空间限制的普通成员函数,可以把它看做是一个类。它与类的实例无关,在调用时不会调用隐含的this指针,因此不能为虚函数。简言之,成员函数实例相关,静态函数类相关,虚函数相当于成员函数,所以静态函数不能为虚函数。

                  (5)在类外定义虚函数,只能在声明时加virtual,不能在类外定义时加virtual。

                  (6)构造函数、拷贝构造函数、赋值运算符重载不能是虚函数。

                         原因:虚函数的作用是通过父类的指针或引用调用子类的相关函数,而构造函数在创建对象时是自己调用的,没有通过子类的指针或引用调用,所以构造函数不可能是虚函数。拷贝构造同理。

       (7)析构函数要定义成虚函数---保证正确调用对应的函数 

                   面向对象的特性---多态

                     正常情况下,子类对象在构造时先调用父类的构造函数,再调用子类的构造函数;析构时先调用子类的析构函数,再调用父类的析构函数。可以理解为子

                类有两部分,一部分是从父类继承的,一部分是自己定义的,初始化的时候先调用父类初始化从父类中继承的,再调用子类初始化自己定义的。析构函数也

                是一样,子类的析构函数只会析构自己定义的一部分,若父类不定义成虚函数,在delete时只会调用父类的析构而不调用子类的析构,最终会导致内存泄漏

                的问题。

        4.普通调用&多态调用

              普通调用:与类型有关,调用速度快

                多态调用:与对象有关,调用速度较普通调用慢

  • 继承体系同名成员函数的关系

        1.重载:

                同一作用域;

                函数名相同;

                参数(类型、个数)不同

        void func()

        {

             std::cout << "func1()" << std::endl;

        }

        void func(int n)

        {

           std::cout << "func12()" << std::endl;

        }

        int main()

        {

             func();//func1

           func(1);//func2

           return 0;

        }

           2.重写(覆盖)

                不同作用域(一个子类,一个父类);

                函数名、参数、返回值相同(协变除外);

                加virtual

        class p1

        {

       public:

           virtual void print()

             {

              std::cout << "p1 _a" << std::endl;

            }

        public:

           int _a;

        };

        class p2:public p1

        {

            public:

           virtual void print()

           {

              std::cout << "p2 _b" << std::endl;

           }

        public:

           int _b;

        };

        void show(p1& p)

        {

           p.print();

        }

        int main()

        {

           p1 a;

           p2 b;

           a.print();//p1 _a

           b.print();//p2_b

           return 0;

        }

        3.重定义(隐藏)

                不同作用域(一个子类,一个父类);

                函数名相同;

                子类或父类中不是重写的就是重定义

  • 虚函数表分析

面向对象的特性---多态

                

虚函数的指针有虚函数表,虚函数表相当于一个指针数组,里边存的是虚函数的函数指针 ,通常VS环境下以0结尾。

下图为子类和父类的虚函数基表:在32位机虚函数表是4字节,在64位机虚函数表是8字节 

            面向对象的特性---多态

所以,上图中父类包含一个虚函数表以及一个整形变量,所以大小为8;子类在继承了父类的基础上自己还有一个整形变量,所以是12。