C/C++知识点之C++语言学习(十三)——C++对象模型分析
小标 2019-04-22 来源 : 阅读 1006 评论 0

摘要:本文主要向大家介绍了C/C++知识点之C++语言学习(十三)——C++对象模型分析,通过具体的内容向大家展示,希望对大家学习C/C++知识点有所帮助。

本文主要向大家介绍了C/C++知识点之C++语言学习(十三)——C++对象模型分析,通过具体的内容向大家展示,希望对大家学习C/C++知识点有所帮助。


一、C++对象模型分析


1、类对象模型的内存布局


class是一种特殊的struct,class与struct遵循相同的内存对齐原则,class中的成员函数与成员变量是分开存放的,每个对象拥有独立的成员变量,所有的对象共享类中的成员函数。
运行时,类对象退化为结构体的形式:
A、所有成员变量在内存中依次排布
B、由于内存对齐的存在,成员变量间可能存在内存间隙
C、可以通过内存地址访问成员变量
D、访问权限关键字在运行时失效


#include

using namespace std;

class A
{
    int i;
    int j;
    char c;
    double d;
public:
    void print()
    {
        cout << "i = " << i << ", "
             << "j = " << j << ", "
             << "c = " << c << ", "
             << "d = " << d << endl;
    }
};

struct B
{
    int i;
    int j;
    char c;
    double d;
};

int main(int argc, char *argv[])
{
    A a;
    //64 bit machine
    cout << "sizeof(A) = " << sizeof(A) << endl;    // 24
    cout << "sizeof(a) = " << sizeof(a) << endl;    // 24
    cout << "sizeof(B) = " << sizeof(B) << endl;    // 24

    a.print();

    B* p = reinterpret_cast(&a);

    p->i = 1;
    p->j = 2;
    p->c = 'c';
    p->d = 3.14;
    a.print();

    return 0;
}


上述代码中,class A对象与struct B对象在内存中的排布相同。


2、派生类类对象模型


子类是由父类成员叠加子类成员得到的。


#include

using namespace std;

class Parent
{
protected:
    int m_i;
    int m_j;
};

class Child : public Parent
{
public:
    Child(int i, int j, double d)
    {
        m_i = i;
        m_j = j;
        m_d = d;
    }
    void print()
    {
        cout << "m_i = "<< m_i << endl;
        cout << "m_j = "<< m_j << endl;
        cout << "m_d = "<< m_d << endl;
    }
private:
    double m_d;
};

struct Test
{
    int i;
    int j;
    double d;
};

int main(int argc, char *argv[])
{
    cout << sizeof(Parent) << endl;//8
    cout << sizeof(Child) << endl;//16
    Child child(1,2,3.14);
    child.print();
    Test* test = reinterpret_cast(&child);
    cout << "i = " << test->i << endl;
    cout << "j = " << test->j << endl;
    cout << "d = " << test->d << endl;

    test->i = 100;
    test->j = 200;
    test->d = 3.1415;
    child.print();
    return 0;
}


二、C++多态的实现机制


1、C++多态的实现简介


当类中声明虚函数时,C++编译器会在类中生成一个虚函数表。虚函数表是一个用于存储virtual成员函数地址的数据结构。虚函数表由编译器自动生成与维护,virtual成员函数会被编译器放入虚函数表中。存在虚函数时,每个对象中都有一个指向类的虚函数表的指针。
由于对象调用虚函数时会查询虚函数表,因此虚函数的调用效率比普通成员函数低。
当创建类对象时,如果类中存在虚函数,编译器会在类对象中增加一个指向虚函数表的指针。父类对象中虚函数表存储的是父类的虚函数,子类对象中虚函数表存储的是子类对象的虚函数。虚函数表指针存储在类对象存储空间的开始的前4(8)个字节。


2、虚函数表


如果一个类包含虚函数,其类包含一个虚函数表。
如果一个基类包含虚函数,基类会包含一个虚函数表,其派生类也会包含一个自己的虚函数表。
虚函数表是一个函数指针数组,其数组元素是虚函数的函数指针,每个元素对应一个虚函数的函数指针。非虚成员函数的调用并不需要经过虚函数表,所以虚函数表的元素并不包括非虚成员函数的函数指针。 
虚函数表中虚函数指针的赋值发生在编译器的编译阶段,即在代码编译阶段虚函数表就生成。


#include

using namespace std;

class Parent
{
public:
    Parent(int i, int j)
    {
        m_i = i;
        m_j = j;
    }
    virtual void print()
    {
        cout << "Parent::" << __func__<< endl;
        cout << "m_i = "<< m_i << endl;
        cout << "m_j = "<< m_j << endl;
    }
    virtual double sum()
    {
        cout << "Parent::" << __func__<< endl;
        double ret = m_i + m_j;
        cout <<ret << endl;
        return ret;
    }
    virtual void display()
    {
        cout << "Parent::display()" << endl;
    }
protected:
    int m_i;
    int m_j;
};

class Child : public Parent
{
public:
    Child(int i, int j, double d):Parent(i, j)
    {
        m_d = d;
    }
    virtual void print()
    {
        cout << "Child::" << __func__<< endl;
        cout << "m_i = "<< m_i << endl;
        cout << "m_j = "<< m_j << endl;
        cout << "m_d = "<< m_d << endl;
    }
    virtual double sum()
    {
        cout << "Child::" << __func__<< endl;
        double ret = m_i + m_j + m_d;
        cout << ret << endl;
        return ret;
    }
private:
    void display()
    {
        cout << "Child::display()" << endl;
    }
private:
    double m_d;
};

struct Test
{
    void* vptr;
    int i;
    int j;
    double d;
};

int main(int argc, char *argv[])
{
    cout << sizeof(Parent) << endl;//12
    cout << sizeof(Child) << endl;//24
    Child child(1,2,3.14);
    Test* test = reinterpret_cast(&child);
    cout << "virtual Function Table Pointer:" << endl;
    cout << "vptr = " << test->vptr << endl;
    //虚函数表指针位于类对象的前4字节
    cout << "child Object address: " << &child << endl;
    cout << "Member Variables Address: " << endl;
    cout << "&vptr = " << &test->vptr << endl;
    cout << "&i = " << &test->i << endl;
    cout << "&j = " << &test->j << endl;
    cout << "&d = " << &test->d << endl;

    //函数指针方式访问类的虚函数
    cout << "Virtual Function Table: " << endl;
    cout << "Virtual print Function Address: " << endl;
    cout << (long*)(*((long *)(*((long *)&child)) + 0)) <<endl;
    cout << "Virtual sum Function Address: " << endl;
    cout << (long*)(*((long *)(*((long *)&child)) + 1)) <<endl;
    cout << "Virtual display Function Address: " << endl;
    cout << (long*)(*((long *)(*((long *)&child)) + 2)) <<endl;
    typedef void (*pPrint)();
    pPrint print = (pPrint)(*((long *)(*((long *)&child)) + 0));
    print();

    typedef double (*pSum)(void);
    pSum sum = (pSum)(*((long *)(*((long *)&child)) + 1));
    sum();

    typedef void (*pDisplay)(void);
    pDisplay display = (pDisplay)(*((long *)(*((long *)&child)) + 2));
    display();
    return 0;
}


上述代码中,通过类对象的虚函数表指针可以访问类的虚函数表,虚函数表顺序存储了类的虚函数的函数地址,通过函数指针的方式可以调用类的虚函数,包括声明为private的虚函数。但由于使用函数指针方式访问类的虚函数时,类的虚函数在执行过程中其this指针指向的对象是不确定的,因此访问到的类对象的成员变量的值是垃圾值。


3、虚函数表指针


虚函数表属于类,而不是属于某个具体的类对象,一个类只需要一个虚函数表。同一个类的所有对象都使用类的唯一虚函数表。 为了指定类对象的虚函数表,类对象内部包含一个指向虚函数表的指针,指向类的虚函数表。为了让每个类对象都拥有一个虚函数表指针,编译器在类中添加了一个指针*__vptr,用来指向虚函数表。当类对象在创建时便拥有__vptr指针,且__vptr指针的值会自动被设置为指向类的虚函数表。


class Parent
{
public:
    Parent(int i, int j)
    {
        m_i = i;
        m_j = j;
    }
    virtual void print()
    {
        cout << "Parent::" << __func__<< endl;
        cout << "m_i = "<< m_i << endl;
        cout << "m_j = "<< m_j << endl;
    }
    virtual double sum()
    {
        cout << "Parent::" << __func__<< endl;
        double ret = m_i + m_j;
        cout <<ret << endl;
        return ret;
    }
    virtual void display()
    {
        cout << "Parent::display()" << endl;
    }
    int add(int value)
    {
        return m_i + m_j + value;
    }
protected:
    void func()
    {

    }
protected:
    int m_i;
    int m_j;
};


上述代码中,类的虚函数表如下:
类Parent对象的内存布局中,虚函数表指针位于类对象存储空间的开头,其值0X409004是类Parent的虚函数表的首地址,虚函数表中的第一个数组元素是虚函数Parent::print的地址,第二个数组元素是虚函数Parent::sum,第三个数组元素是虚函数Parent::display,非虚函数不在虚函数表中。

4、类对象的内存布局


对于含有虚函数的类,虚函数表指针位于类对象内存布局的开始位置,然后依次排列类继承自父类的成员变量,最后依次排列类自身的非静态成员变量。


#include

using namespace std;

class Parent
{
public:
    Parent(int i, int j)
    {
        m_i = i;
        m_j = j;
    }
    virtual void print()
    {
        cout << "Parent::" << __func__<< endl;
        cout << "m_i = "<< m_i << endl;
        cout << "m_j = "<< m_j << endl;
    }
    virtual double sum()
    {
        cout << "Parent::" << __func__<< endl;
        double ret = m_i + m_j;
        cout <<ret << endl;
        return ret;
    }
    virtual void display()
    {
        cout << "Parent::display()" << endl;
    }
    int add(int value)
    {
        return m_i + m_j + value;
    }
protected:
    void func()
    {

    }
protected:
    int m_i;
    int m_j;
    static int m_count;
};
int Parent::m_count  = 0;

class ChildA : public Parent
{
public:
    ChildA(int i, int j, double d):Parent(i, j)
    {
        m_d = d;
    }
    virtual void print()
    {
        cout << "ChildA::" << __func__<< endl;
        cout << "m_i = "<< m_i << endl;
        cout << "m_j = "<< m_j << endl;
        cout << "m_d = "<< m_d << endl;
    }
    virtual double sum()
    {
        cout << "ChildA::" << __func__<< endl;
        double ret = m_i + m_j + m_d;
        cout << ret << endl;
        return ret;
    }
private:
    void display()
    {
        cout << "ChildA::display()" << endl;
    }
private:
    double m_d;
};

class ChildB : public Parent
{
public:
    ChildB(int i, int j, double d):Parent(i, j)
    {
        m_d = d;
    }
    virtual void print()
    {
        cout << "ChildB::" << __func__<< endl;
        cout << "m_i = "<< m_i << endl;
        cout << "m_j = "<< m_j << endl;
        cout << "m_d = "<< m_d << endl;
    }
    virtual double sum()
    {
        cout << "ChildB::" << __func__<< endl;
        double ret = m_i + m_j + m_d;
        cout << ret << endl;
        return ret;
    }
private:
    void display()
    {
        cout << "ChildB::display()" << endl;
    }
private:
    double m_d;
};

struct ParentTest
{
    void* vptr;
    int i;
    int j;
};

struct ChildTest
{
    void* vptr;
    int i;
    int j;
    double d;
};

int main(int argc, char *argv[])
{
    cout << sizeof(Parent) << endl;//12
    cout << sizeof(ChildA) << endl;//24
    cout << endl;
    cout << "Parent..." <<endl;
    Parent parent(1,2);
    ParentTest* parenttest = reinterpret_cast(&parent);
    cout << "Member Variable Value:"<< endl;
    //虚函数表的首地址
    cout << parenttest->vptr << endl;//编译时确定
    cout << parenttest->i << endl;//1
    cout << parenttest->j << endl;//2
    cout << "Member Variable Address:" << endl;
    cout << &parenttest->vptr << endl;
    cout << &parenttest->i << endl;
    cout << &parenttest->j << endl;
    cout << endl;
    cout << "Child..." << endl;
    ChildA child(1,2,3.14);
    ChildTest* childtest = reinterpret_cast<C    

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标编程语言C/C+频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程