虚函数以及类的强制转换

虚函数

类的对象模型

class A{
public:
    void f1(){}
    void f2(){} 
    virtual void f3(){}
    virtual void f4(){}
};

class B{
public:
    void f1(){}
    void f2(){} 
};

class C{

};

int main()
{
    A a;
    B b;
    C c;
    std::cout<<sizeof(a)<<std::endl;
    std::cout<<sizeof(b)<<std::endl;
    std::cout<<sizeof(c)<<std::endl;
    return 0;
}

输出:

我们可以发现空类和只有多个函数的类的对象占的字节都是1
多个虚函数的类的对象占得字节都是8(不同编译环境,这个值不同)

这是因为在一个类中引入一个或多个虚函数时,编译器会插入一个虚函数表指针vptr
且每一个有虚函数的类都有一个虚函数表
类似于如下代码:

class A{
public:
  void* vptr;
};

因为成员变量是占用类的对象的内存空间的,所以sizeof(a)=8

在根据下面这个代码解释一下类A的对象在内存中的布局

class A{
private:
    int m_a;
    int m_b;
public:
    void func1(){}
    void func2(){} 
    virtual void vfunc(){};
    virtual void vfunc2(){}
    virtual ~A(){}
};
int main()
{
    A a;
    std::cout<<sizeof(a)<<std::endl; //输出结果为16,因为两个int(8)+一个virtual(8)
    return 0;
}


虚函数表指针指向虚函数表,虚函数表并不占用类的对象的内存,就像func1()和func()不占用内存一样

多态的使用

# include<iostream>

class Base{
public:
    std::string ISBN;
    virtual void f(){}
    virtual void g(){  //使用virtual,派生类各自定义函数f
        std::cout<<"book"<<std::endl;
    }
    virtual void h(){}
};

class dpar1:public Book{
public:
    void g()
    {
        std::cout<<"ComicBook"<<std::endl;
    }
};

class dpar2:public Book{
public:
    void g()
    {
        std::cout<<"ActionBook"<<std::endl;
    }
};

int main()
{
    dpar1 cb;
    dpar2 ab;
    
    //使用基类引用或指针调用虚函数时,发生动态绑定
    //根据绑定对象的类型,选择函数的版本
    Base* b1 = (Base*)&cb;
    Base &b2 = ab;
    b1->f();
    b2.f();
    return 0;
}

输出结果:


如图,父类Base有三个函数,子类Derive重写了父类的g函数(粉色的部分)

在图中,子类Derive的f和h都指向了父类的两个虚函数,只有g指向了自己重写的虚函数

当子类对象的地址赋值给父类对象的指针时,父类对象的vptr已经是子类的vptr了
所以在执行函数g时是查的子类的虚函数表,执行的是子类重写的函数g

类的强制转换

在上面的例子中,ComicBook可以强制转换为Book

其实在类的强制转换时只转换了基类和派生类共同拥有的东西,在上面的例子中就是ISBN和f()

但如果没有共同拥有的东西(不是父类和子类的关系),则不能进行强制转换

class A{

};

class B{

};

int main()
{
    A a;B b;
    a = (A)b;
}

报错:

posted @ 2024-03-18 17:06  拾墨、  阅读(8)  评论(0编辑  收藏  举报