虚函数总结

来源:互联网 发布:电脑开热点软件 编辑:程序博客网 时间:2024/05/21 05:55

多态(Polymorphism),是面向对象程序设计(OOP,Object-Oriented Programming)的重要特征。

多态的定义:

多态的实现方式有:

(1)函数重载 

(2)运算符重载 

(3)模版  

(4)虚函数

前三种我们称为静态绑定,第四种称为动态绑定。

静态绑定:绑定过程出现在编译阶段,在编译期就已确定要调用的函数;

动态绑定:绑定过程工作在运行阶段时执行,在程序运行时才确定将要调用的函数。

(只有通过基类指针或引用调用虚函数才能引发动态绑定)

虚函数的概念:在基类中冠以关键字“virtual”的成员函数。

虚函数的定义:

(1)virtual 函数返回值类型函数名称 (参数列表)

(2)如果一个函数在基类中被声明为虚函数,则他在所有派生类中都是虚函数。

值得注意的是:

虚函数不能声明为静态函数,也不能是友元函数。

基类指针指向派生类对象:

////  VF2.cpp//  C++////  Created by Cheng Sun on 2017/6/21.//  Copyright © 2017年 Cheng Sun. All rights reserved.//#include <stdio.h>#include <iostream>using namespace std;class Base{public:    virtual void Func1()    {        cout << "Base::Func1()..." << endl;    }    virtual void Func2()    {        cout << "Base::Func2()..." << endl;    }    void Func3()    {        cout << "Base::Func3()..." << endl;    }};class Derived : public Base{public:    /*virtual*/ void Func1()    {        cout << "Derived::Func1()..." << endl;    }    /*virtual*/ void Func2()    {        cout << "Derived::Func2()..." << endl;    }    void Func3()    {        cout << "Derived::Func3()..." << endl;    }};int main(){    Base* p;    Derived d;    p = &d; // 基类指针指向派生类对象        p->Func1(); //Func1是虚函数,基类指针指向派生类对象,调用的是派生类对象的虚函数。    p->Func2();    p->Func3(); //Func3非虚函数,根据p指针实际类型来调用相应类的成员函数。        return 0;}
程序运行结果:

Derived::Func1()...Derived::Func2()...Base::Func3()...Program ended with exit code: 0


虚析构函数

同样的道理,也反映在析构函数的应用上。当基类指针指向派生类对象,我们delete基类指针时,若基类的析构函数的不是虚函数,则只会释放基类对象的内存空间。想要同时释放派生类的内存空间,则需要将析构函数设为虚函数。(同样的,只需要将基类的析构函数设为虚函数,则派生类的析构函数也会自动变成虚函数。)

////  VF2.cpp//  C++////  Created by Cheng Sun on 2017/6/21.//  Copyright © 2017年 Cheng Sun. All rights reserved.//#include <stdio.h>#include <iostream>using namespace std;class Base{public:    virtual void Func1()    {        cout << "Base::Func1()..." << endl;    }    virtual void Func2()    {        cout << "Base::Func2()..." << endl;    }    void Func3()    {        cout << "Base::Func3()..." << endl;    }    Base()    {        cout << "Base()..." << endl;    }    virtual ~Base()    {        cout << "~Base()..." << endl;    }};class Derived : public Base{public:    /*virtual*/ void Func1()    {        cout << "Derived::Func1()..." << endl;    }    /*virtual*/ void Func2()    {        cout << "Derived::Func2()..." << endl;    }    void Func3()    {        cout << "Derived::Func3()..." << endl;    }    Derived()    {         cout << "Derived()..." << endl;    }    ~Derived()    {        cout << "~Derived()..." << endl;    }};int main(){    Base* p;    p = new Derived; // 创建派生类对象的时候,会首先创建它的基类对象        p->Func1();        delete p;        return 0;}
程序运行结果:

Base()...Derived()...Derived::Func1()...~Derived()...~Base()...Program ended with exit code: 0

如果上面代码中的基类没有将析构函数设为虚函数,那么delete p只会执行~Base(),而不会执行~Derived()。

总的来说,什么时候需要用到虚析构函数呢?如果一个类要作为多态基类,那么就要将析构函数定义为虚函数。

反之,如果一个类不会被任何派生类继承,则没有必要将它的析构函数定义为虚函数。

虚表指针和虚函数表

当一个类包含一个或一个以上的虚函数时,编译器就会为这个类产生一个4个字节(编译器工作在32位的框架下)的虚表指针。

虚函数是通过虚表来实现的。讨论没有覆盖的虚函数的情况没有太多意义,我们讨论一下有覆盖的虚函数的工作机制。当一个派生类继承基类时,若是派生类中出现了基类中同名的虚函数,即发生了所谓的覆盖,那么在派生类的虚函数表中,覆盖的虚函数被放到了原本父类虚函数的位置,没有被覆盖的函数,位置依旧。

虚表指针存放在类对象内部的头四个字节,三大区域都有可能。

虚函数表存放在全局数据区(静态区),因为对每个类,所有该类的对象共用虚表。

 

为方便下面部分代码的理解,这里稍稍介绍一下定义一个函数指针的方法:

返回类型 (*指针名) (函数参数列表)

例如: void (*Fun)(void) 意思是定一个指向返回值为void,函数参数也为void的函数的指针。

 

探讨包含虚函数的基类和派生类的内存模型:

////  VF3.cpp//  C++////  Created by Cheng Sun on 2017/6/21.//  Copyright © 2017年 Cheng Sun. All rights reserved.//#include <stdio.h>#include <iostream>using namespace std;class Base // 4 Bytes (pointer to virtual table) + 4 Bytes (int) = 8 Bytes{public:    virtual void Func1()    {        cout << "Base::Func1()..." << endl;    }        virtual void Func2()    {        cout << "Base::Func2()..." << endl;    }        virtual void Func3()    {        cout << "Base::Func3()..." << endl;    }        int data1_;};class Derived : public Base // 4 Bytes (pointer to virtual table) + 4 Bytes (int, data1_) + 4 Bytes (int, data2_) = 12 Bytes{public:    void Func2()    {        cout << "Derived::Func2()..." << endl;    }        void Func3()    {        cout << "Derived::Func3()..." << endl;    }        int data2_;};typedef void (*FUNC) (void); //定义一个函数指针,指向一个函数,这函数的类型是 void Fun (void)int main(){    cout << "size of Base = " << sizeof(Base) << endl;    cout << "size of Derived = " << sizeof(Derived) << endl;        Base b;    Derived d;        long** p = (long**)&b; //定义一个p, 指向虚表指针的指针    FUNC fun;        cout << "基类的虚函数表如下: " << endl;    fun = (FUNC)p[0][0];    fun(); //函数指针 == 函数地址 == 函数名 ?        fun = (FUNC)p[0][1];    fun();        fun = (FUNC)p[0][2];    fun();        cout << "派生类的虚函数表如下: " << endl;    p = (long**)&d;    fun = (FUNC)p[0][0];    fun();        fun = (FUNC)p[0][1];    fun();        fun = (FUNC)p[0][2];    fun();        cout << "----------------" << endl;    Base* pp = &d;    pp->Func2(); // 基类指针指向派生对象,通过基类指针(或引用)调用派生类虚函数,触发动态绑定    d.Func2();   // 直接调用,不会触发动态绑定,属于静态绑定        return 0;}
程序运行结果:
size of Base = 8size of Derived = 12基类的虚函数表如下: Base::Func1()...Base::Func2()...Base::Func3()...派生类的虚函数表如下: Base::Func1()...Derived::Func2()...Derived::Func3()...----------------Derived::Func2()...Derived::Func2()...Program ended with exit code: 0


多重继承中,包含虚函数的基类和派生类的内存模型:

因为是多重继承,不同于单一继承,所以派生类的虚表指针也不止一个,所以代码中可以访问p[1][n],p[2][n],而如果是单一继承,则只有p[0][n]。

验证代码如下:

////  VF3.cpp//  C++////  Created by Cheng Sun on 2017/6/21.//  Copyright © 2017年 Cheng Sun. All rights reserved.//#include <stdio.h>#include <iostream>using namespace std;class Base1 // 4 Bytes (pointer to virtual table) + 4 Bytes (int) = 8 Bytes{public:    virtual void Func1()    {        cout << "Base1::Func1()..." << endl;    }        virtual void Func2()    {        cout << "Base1::Func2()..." << endl;    }        virtual void Func3()    {        cout << "Base1::Func3()..." << endl;    }        int data1_ = 1;};class Base2 // 4 Bytes (pointer to virtual table) + 4 Bytes (int) = 8 Bytes{public:    virtual void Func1()    {        cout << "Base2::Func1()..." << endl;    }        virtual void Func2()    {        cout << "Base2::Func2()..." << endl;    }        virtual void Func3()    {        cout << "Base2::Func3()..." << endl;    }        int data2_ = 2;};class Base3 // 4 Bytes (pointer to virtual table) + 4 Bytes (int) = 8 Bytes{public:    virtual void Func1()    {        cout << "Base3::Func1()..." << endl;    }        virtual void Func2()    {        cout << "Base3::Func2()..." << endl;    }        virtual void Func3()    {        cout << "Base3::Func3()..." << endl;    }        int data3_ = 3;};class Derived : public Base1, public Base2, public Base3// 4 Bytes (pointer to virtual table) * 3 + 4 Bytes (int, data1_) * 3 + 4 Bytes (int, data4_) = 28 Bytes{public: // four virtual funcitons in derived class    void Func2()    {        cout << "Derived::Func2()..." << endl;    }        void Func3()    {        cout << "Derived::Func3()..." << endl;    }        virtual void  Func4()    {        cout << "Derived::Func4()... 此函数是派生类特有的哦,只存在派生类覆盖第一个基类的虚表里" << endl;    }    int data4_ = 4;};typedef void (*FUNC) (void); //定义一个函数指针,指向一个函数,这函数的类型是 void Fun (void)int main(){    cout << "size of Base = " << sizeof(Base1) << endl;    cout << "size of Derived = " << sizeof(Derived) << endl;            long** p;    FUNC fun;    int* da;        //既然当一个类包含虚函数时,系统就会为它自动生成一个虚函数表    //那么一个类就一个表?如果对于继承了好几个基类的派生类呢?    //如果不是,那么继承了多个基类的派生类有多少个虚表指针?    cout << "-----------------" << endl;    cout << "第一个基类的虚函数表如下: " << endl;    Base1 b1;    p = (long**)&b1;        fun = (FUNC)p[0][0];    fun();        fun = (FUNC)p[0][1];    fun();        fun = (FUNC)p[0][2];    fun();    cout << "-----------------" << endl;        da = (int*)p[1];    cout << "data1_ = " << da << endl;        cout << "-----------------" << endl;    cout << "第二个基类的虚函数表如下: " << endl;    Base2 b2;    p = (long**)&b2;        fun = (FUNC)p[0][0];    fun();        fun = (FUNC)p[0][1];    fun();        fun = (FUNC)p[0][2];    fun();        cout << "-----------------" << endl;        da = (int*)p[1];    cout << "data2_ = " << da << endl;        cout << "-----------------" << endl;    cout << "第三个基类的虚函数表如下: " << endl;    Base3 b3;    p = (long**)&b3;        fun = (FUNC)p[0][0];    fun();        fun = (FUNC)p[0][1];    fun();        fun = (FUNC)p[0][2];    fun();    cout << "-----------------" << endl;    da = (int*)p[1];    cout << "data3_ = " << da << endl;        cout << "-----------------" << endl;        cout << "派生类的虚函数表如下: " << endl;    Derived d;    p = (long**)&d; //定义一个p, 指向虚表指针的指针        fun = (FUNC)p[0][0];    fun(); //函数指针 == 函数地址 == 函数名 ?        fun = (FUNC)p[0][1];    fun();        fun = (FUNC)p[0][2];    fun();        fun = (FUNC)p[0][3];    fun();        cout << "-----------------" << endl;        // 为什么跳过p[1][n]呢,因为p[1][0]存放的是 data1_,虽然数据成员并没有存在虚表里面,但是派生类因为继承的关系,依然继承了基类里面的数据成员,所以通过指针访问虚表指针进而调用虚函数时,要跳过派生类中继承的基类的数据成员的内存存储空间(位置)    // 验证一下:    da = (int*)p[1];    cout << "data1_ = " << da << " 成员数据并不存放于虚表中" << endl;    cout << "-----------------" << endl;    fun = (FUNC)p[2][0];    fun();        fun = (FUNC)p[2][1];    fun();        fun = (FUNC)p[2][2];    fun();        cout << "-----------------" << endl;    da = (int*)p[3];    cout << "data2_ = " << da << " 成员数据并不存放于虚表中" << endl;    //fun = (FUNC)p[2][3];    //fun();   //报错,说明继承了多个基类的派生类,它里面独有的虚函数,只存在第一个(按声明顺序)继承的基类的虚表中!!!        // 为什么跳过p[1][n]呢,因为p[1][0]存放的是 data2_    cout << "-----------------" << endl;    fun = (FUNC)p[4][0];    fun();        fun = (FUNC)p[4][1];    fun();        fun = (FUNC)p[4][2];    fun();        cout << "-----------------" << endl;    da = (int*)p[5];    cout << "data3_ = " << da << " 成员数据并不存放于虚表中" << endl;    cout << "-----------------" << endl;    da = (int*)p[6];    cout << "data4_ = " << da << " 成员数据并不存放于虚表中" << endl;    //fun = (FUNC)p[4][3];    //fun();  //报错,同理。        return 0;}

程序运行结果:

size of Base = 8size of Derived = 28-----------------第一个基类的虚函数表如下: Base1::Func1()...Base1::Func2()...Base1::Func3()...-----------------data1_ = 0x1-----------------第二个基类的虚函数表如下: Base2::Func1()...Base2::Func2()...Base2::Func3()...-----------------data2_ = 0x2-----------------第三个基类的虚函数表如下: Base3::Func1()...Base3::Func2()...Base3::Func3()...-----------------data3_ = 0x3-----------------派生类的虚函数表如下: Base1::Func1()...Derived::Func2()...Derived::Func3()...Derived::Func4()... 此函数是派生类特有的哦,只存在派生类覆盖第一个基类的虚表里-----------------data1_ = 0x1 成员数据并不存放于虚表中-----------------Base2::Func1()...Derived::Func2()...Derived::Func3()...-----------------data2_ = 0x2 成员数据并不存放于虚表中-----------------Base3::Func1()...Derived::Func2()...Derived::Func3()...-----------------data3_ = 0x3 成员数据并不存放于虚表中-----------------data4_ = 0x4 成员数据并不存放于虚表中Program ended with exit code: 0








原创粉丝点击