笔记:C++虚函数

  C++的多态性分为两种:静态多态(编译时多态)和动态多态(运行时多态)。静态多态主要通过重载和模板来实现,动态多态是子类覆盖父类的虚函数,定义一个父类的指针,使指针指向子类实例,这样,父类就可以访问子类的方法,如果把父类的指针指向不同的子类实例,父类就可以访问不同的方法。

class Base 
{
public:
    int data;
    void myFunc() { cout << "myFunc" << endl; }
    virtual void f() { cout << "Base::f()" << endl; }
    virtual void g() { cout << "Base::g()" << endl; }
    virtual void h() { cout << "Base::h()" << endl; }
};

class Derive : public Base 
{
public:
    virtual void g() { cout << "Derive::g()" << endl; }
};

Base b;
Base *ptr = new Derive();
ptr->g();    //Derive::g()

  在创建带有虚函数的类的实例时,会创建一个指向虚函数表的指针,这个指针被存在实例的开始处,虚函数表中保存着虚函数的地址,如图:
 

  这里写图片描述

  我们可以这样获取到虚函数的地址:&b表示类的首地址,由于类内存表中第一个位置存的是指针,所以先把地址强制转换成指针类型,就可以取出第一个值 *(int*)(&b),这个值就是指向虚函数的指针vptr(g++中指向虚函数表的指针放在开始处),vptr就是虚函数表的首地址,同理,虚函数表中存的是指针,所以强制转换后取值 *(int*)*(int*)(&b)就是函数 f 的地址, *((int*)*(int*)(&b)+1) 就是 g 的地址。要想输出虚函数,需要强制转换成函数指针。

    typedef void(*pfunc)(void);
    Base b;
    pfunc p;
    p = (pfunc)*((int*)*(int*)(&b)+1); //强制转换成函数指针
    p();  //Base::g();

注:忽略了一点,64位时指针是8个字节,而不是4个字节,所以,上述int*中的int改为long更恰当。

  
  
  子类继承基类时,虚函数表中会包含子类和基类的全部虚函数

class Derive : public Base
{
public:
    virtual void df() { cout << "Derive::df()" << endl;
    virtual void dg() { cout << "Derive::dg()" << endl;
    virtual void dh() { cout << "Derive::dh()" << endl;
}

这里写图片描述

  
  如果子类的虚函数覆盖了父类的虚函数,虚函数表中父类的虚函数将会被子类的虚函数替代。

class Derive : public Base
{
public:
    virtual void f() { cout << "Derive1::f()" << endl;
    virtual void dg() { cout << "Derive1::dg()" << endl;
    virtual void dh() { cout << "Derive1::dh()" << endl;
}

Derive d;
Base * b = &d;
b->f();  //Derive::f()

这里写图片描述

  所以,前面的例子中,父类的指针Base * ptr = new Derive(),访问 f 方法时(ptr->f()),访问到的是子类的方法。
  多重继承时,有几个含有虚函数的父类,就会创建几个指向虚函数表的指针,每个虚函数表都存着对应的父类的虚函数的地址,其中,子类的虚函数地址保存在第一个父类的虚函数表中。

class Base1 
{
public:
    virtual void f() { cout << "Base1::f()" << endl; }
    virtual void g() { cout << "Base1::g()" << endl; }
    virtual void h() { cout << "Base1::h()" << endl; }
};
class Base2 
{
public:
    virtual void f() { cout << "Base2::f()" << endl; }
    virtual void g() { cout << "Base2::g()" << endl; }
    virtual void h() { cout << "Base2::h()" << endl; }
};
class Derive : public Base1, public Base2 
{
public:
    virtual void df() { cout << "Derive::df()" << endl; 
};

Derive d;
Base1 * ptr1 = &d;
Base2 * ptr2 = &d;
ptr1->f();  //Base1::f()
ptr2->f();  //Base2::f()

这里写图片描述

  两个父类Base1和Base2的指针虽然都指向子类,但是其实地址是不同的,ptr1对应的是vptr1指向的对象,ptr2对应的是vptr2指向的对象。
  
  子类覆盖父类方法时:

class Derive : public Base1, public Base2 
{
public:
    virtual void f() { cout << "Derive::f()" << endl;     
}

Derive d;
Base1 * ptr1 = &d;
Base2 * ptr2 = &d;
ptr1->f();  //Derive::f()
ptr2->f();  //Derive::f()

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值