C++对象模型

C++继承内存对象模型 最近研究了一下,C++继承的内存对象模型。主要是读了读http://blog.youkuaiyun.com/haoel/article/details/3081328(C++ 对象的内存布局)。很推荐这篇文章。          对这篇文章做了做总结。本文的大部分内容来自于这篇文章中的总结 阅读详情

1、简单模型

1.1、概述

C++类中有两种数据成员:static、nonstatic;三种成员函数:static、nonstatic、virtual。它们在内存中的布局方式和访问方式是不同的。像nonstatic对象是在类对象内存储,虚函数是通过一个虚表指针指向一个虚表再通过虚表查找。静态和非静态函数都在对象之外存储。

1.2、对象模型图例

定义一个C++类:
这里写图片描述

class Base
{
public:
    Base(int);
    virtual ~Base(void);
    virtual void base_print() const;

    static void baseStaticFunc();
    void baseNonStaticFunc();

protected:
    int iBase;
    static int iSatic;
};

Base类中各种成员如下图方式存储、访问:
这里写图片描述
不同编译存储各个成员可能有所差异,这是在Windows+VS2012中的布局。
可以看出Base对象中第一项是虚表指针,这个指针指向一张虚表,虚表中存储的是Base类各个虚函数的存储地址。在紧邻虚表的前一个位置是一个typeinfoPtr指针,它指向的是Base类的type_info信息。Base类的静态和非静态成员函数,静态成员数据在Base对象外存储。

1.3、验证模型

以下代码验证1.2图中模型:

void Base_Model()
{
    cout << "********************Base b Model********************" << endl;
    Base b(3);
    cout << "对象b的内存地址 &b = " << &b << endl;
    cout << "对象b中第一项 _vptr Base 虚表指针的值也即虚表首地址 = " << hex << *(int*)(&b) << endl;
    cout << "对象b中第二项 int Base::iBase的值 = " << hex << *((int*)(&b) + 1) << endl;

    cout << "虚函数表中第一个slot的值也即虚函数Base::~Base()的入口地址 = " << hex << *(int*)(*(int*)(&b)) << endl;
    cout << "虚函数表中第二个slot的值也即虚函数Base::base_print()的入口地址 = " << hex << *((int*)(*(int*)(&b)) + 1) << endl;

    pFun pBasePrint = (pFun)(*((int*)(*(int*)(&b)) + 1));       
    pBasePrint();   //注意这里输出是不一样的,这个调用仅仅是调用那段代码但是里面的iBase值不确定,因为iBase在Base对象中
    b.base_print();

    cout << "typeinfoPtr指针的存储位置 = " << ((*(int*)(&b)) - 1) << endl;
    cout << "typeinfoPtr指针的值也即Base's type_info的首地址 = " << hex << *((int*)(*(int*)(&b)) - 1) << endl;

//  cout << hex << Base::baseStaticFunc << endl;
//  cout << hex << Base::baseStaticFunc2 << endl;
//  cout << hex << &Base::baseNonStaticFunc << endl;
//  typedef void (Base::*pmf)();
//  pmf pt = &Base::baseNonStaticFunc;
//  cout << hex << pt << endl;
}

运行结果:
这里写图片描述

2、对象模型with单继承

2.1、无重写单继承

无重写指的是派生类没有重写基类的虚函数。定义一个Base的派生类(除析构函数外无重写):
这里写图片描述

class Derived : public Base
{
public:
    Derived(int);
    virtual ~Derived(void);
    virtual void derived_print(void);

protected:
    int iDerived;
};

则派生类Derived及对象的布局方式如下图:
这里写图片描述
可以看到父类的虚函数放在了虚函数表的前面,后面放的是子类新加入的虚函数,子类的虚析构函数覆盖了父类的虚析构函数。
验证模型:

void DerivedInheritBase()
{
    cout << "********************Derived d Model********************" << endl;
    Derived d(6);
    cout << "对象d的内存地址 &d = " << &d << endl;
    cout << "对象d中第一项 _vptr Derived 虚表指针的值也即虚表首地址 = " << hex << *(int*)(&d) << endl;
    cout << "对象d中第二项 int Base::iBase的值 = " << hex << *((int*)(&d) + 1) << endl;
    cout << "对象d中第三项 int Derived::iDerived的值 = " << hex << *((int*)(&d) + 2) << endl;

    cout << "虚函数表中第一个slot的值也即虚函数Derived::~Derived()的入口地址 = " << hex << *(int*)(*(int*)(&d)) << endl;
    cout << "虚函数表中第二个slot的值也即虚函数Base::base_print()的入口地址 = " << hex << *((int*)(*(int*)(&d)) + 1) << endl;
    cout << "虚函数表中第二个slot的值也即虚函数Derived::derived_print()的入口地址 = " << hex << *((int*)(*(int*)(&d)) + 2) << endl;

    pFun pBasePrint = (pFun)(*((int*)(*(int*)(&d)) + 1));       
    pBasePrint();   
    d.base_print();
    pFun pDerivdPrint = (pFun)(*((int*)(*(int*)(&d)) + 2));
    pDerivdPrint();
    d.derived_print();

    cout << "typeinfoPtr指针的存储位置 = " << ((*(int*)(&d)) - 1) << endl;
    cout << "typeinfoPtr指针的值也即Derived's type_info的首地址 = " << hex << *((int*)(*(int*)(&d)) - 1) << endl;
}

运行结果:
这里写图片描述

2.2、有重写单继承

有重写指的是派生类重写了基类的虚函数。定义一个Base的派生类带有重写(非析构函数):
这里写图片描述

class DerivedWithOverride : public Base
{
public:
    DerivedWithOverride(int i);
    virtual ~DerivedWithOverride(void);

    virtual void derivedWithOverride_print() const;
    virtual void base_print() const;

protected:
    int iDWO;
};

则派生类DerivedWithOverride及对象的布局方式如下图:
这里写图片描述
可以看出子类重写了父类的base_print()函数。
验证模型:

void DerivedOverrideInheritBase()
{
    cout << "********************DerivedWithOverride dow Model********************" << endl;
    DerivedWithOverride dow(9);
    cout << "对象dow的内存地址 &dow = " << &dow << endl;
    cout << "对象dow中第一项 _vptr DerivedWithOverride 虚表指针的值也即虚表首地址 = " << hex << *(int*)(&dow) << endl;
    cout << "对象dow中第二项 int Base::iBase的值 = " << hex << *((int*)(&dow) + 1) << endl;
    cout << "对象dow中第三项 int DerivedWithOverride::iDWO的值 = " << hex << *((int*)(&dow) + 2) << endl;

    cout << "虚函数表中第一个slot的值也即虚函数DerivedWithOverride::~DerivedWithOverride()的入口地址 = " << hex << *(int*)(*(int*)(&dow)) << endl;
    cout << "虚函数表中第二个slot的值也即虚函数DerivedWithOverride::base_print()的入口地址 = " << hex << *((int*)(*(int*)(&dow)) + 1) << endl;
    cout << "虚函数表中第三个slot的值也即虚函数DerivedWithOverride::DerivedWithOverride()的入口地址 = " << hex << *((int*)(*(int*)(&dow)) + 2) << endl;

    pFun pBasePrint = (pFun)(*((int*)(*(int*)(&dow)) + 1));     
    pBasePrint();   
    dow.base_print();
    pFun pDerivdPrint = (pFun)(*((int*)(*(int*)(&dow)) + 2));
    pDerivdPrint();
    dow.derivedWithOverride_print();

    cout << "typeinfoPtr指针的存储位置 = " << ((*(int*)(&dow)) - 1) << endl;
    cout << "typeinfoPtr指针的值也即DerivedWithOverride's type_info的首地址 = " << hex << *((int*)(*(int*)(&dow)) - 1) << endl;
}

运行结果:
这里写图片描述

2.3、虚继承

虚基类是为了解决多重继承中多个间接父类的情况,因此不能使用以上的方法。虚继承的派生类和普通继承派生类的内存结构有很大差异,虚继承的子类有自己的虚函数表,同时也保存了父类的虚函数表,子类数据和父类数据中间用一个分隔符作为分界线。(若派生类没有自己的虚函数,那么派生类就没有虚函数表)
定义一个派生类虚继承自Base类:
这里写图片描述

class DerivedVirtual_1 : virtual public Base
{
public:
    DerivedVirtual_1(int i);
    virtual ~DerivedVirtual_1(void);

    virtual void base_print() const;
    virtual void derivedVirtual_1_print() const;
protected:
    int iDV_1;
};

则派生类DerivedVirtual_1 及对象的布局方式如下图:
这里写图片描述
可以看到,子类的虚函数表指针及数据成员在基类的前面,子类父类之间用分隔符0隔开,子类重写的父类虚函数放在父类虚函数表中。
验证模型:

void DerivedVirtualInheritBase()
{
    cout << "********************DerivedVirtual_1 dv1 Model********************" << endl;
    DerivedVirtual_1 dv1(12);
    cout << "对象dv1的内存地址 &dow = " << &dv1 << endl;
    cout << "对象dv1中第一项 _vptr DerivedVirtual_1 虚表指针的值也即子类虚表首地址 = " << hex << *(int*)(&dv1) << endl;
    cout << "对象dv1中第二项 Ptr 所指内存中的值 = " << dec << *(int*)(*((int*)(&dv1) + 1)) << endl;
    cout << "对象dv1中第三项 int DerivedVirtual_1::iDV_1的值 = " << dec << *((int*)(&dv1) + 2) << endl;
    cout << "对象dv1中第四项 分隔符 0 的值 = " << dec << *((int*)(&dv1) + 3) << endl;

    cout << "子类虚函数表中第一个slot的值也即虚函数DerivedVirtual_1::derivedVirtual_1_print()的入口地址 = " << hex << *(int*)(*(int*)(&dv1)) << endl;

    pFun pDerivedVirtual1Print = (pFun)(*(int*)(*(int*)(&dv1)));        
    pDerivedVirtual1Print();   
    dv1.derivedVirtual_1_print();

    cout << "子类typeinfoPtr指针的存储位置 = " << (*(int*)(&dv1) - 1) << endl;
    cout << "子类typeinfoPtr指针的值也即DerivedVirtual_1's type_info的首地址 = " << hex << *((int*)(*(int*)(&dv1) - 1)) << endl;

    cout << "对象dv1中第五项 _vptr Base 虚表指针的值也即父类虚表首地址 = " << hex << *((int*)(&dv1) + 4) << endl;
    cout << "对象dv1中第六项 int Base::iBase 的值 = " << dec << *((int*)(&dv1) + 5) << endl;

    cout << "父类虚函数表中第一个slot的值也即虚函数DerivedVirtual_1::~DerivedVirtual_1()的入口地址 = " << hex << *(int*)(*((int*)(&dv1) + 4)) << endl;
    cout << "父类虚函数表中第二个slot的值也即虚函数DerivedVirtual_1::base_print()的入口地址 = " << hex << *((int*)(*((int*)(&dv1) + 4)) + 1) << endl;

    pFun pBasePrint = (pFun)(*((int*)(*((int*)(&dv1) + 4)) + 1));       
    pBasePrint();   
    dv1.base_print();
}

运行结果:
这里写图片描述

特例:
(1)Base的派生类DerivedVirtual_1既没有自己的虚函数也没有自己的成员数据,则DerivedVirtual_1的内存布局:
这里写图片描述
测试代码如下:

void DerivedVirtualInheritBase_noNewData() 
{
    cout << "********************DeriveDVirtual_1_noData dv1n Model********************" << endl;
    DeriveDVirtual_1_noData dv1n;

    cout << "分隔符的值 = " << dec << *(int*)(*((int*)(&dv1n))) << endl;
    cout << "父类虚函数表中第二个slot的值也即虚函数Base::base_print()的入口地址 = " << dec << *((int*)(*((int*)(&dv1n) + 1)) + 1) << endl;
    pFun pBasePrint = (pFun)(*((int*)(*((int*)(&dv1n) + 1)) + 1));      
    pBasePrint();   
    dv1n.base_print();
}

(2)Base的派生类DerivedVirtual_1有自己的虚函数,没有自己的成员数据,则DerivedVirtual_1的内存布局:
这里写图片描述
测试代码如下:

//虚继承中子类有自己的虚函数,没有新的成员数据
void DerivedVirtualInheritBase_noNewData()
{
    cout << "********************DeriveDVirtual_1_noData dv1n Model********************" << endl;
    DeriveDVirtual_1_noData dv1n;

    cout << "子类虚函数表中第一个slot的值也即虚函数DeriveDVirtual_1_noData::deriveDVirtual_1_noData_print()的入口地址 = " 
        << hex << *(int*)(*(int*)(&dv1n)) << endl;

    pFun pDerivedVirtual1Print = (pFun)(*(int*)(*(int*)(&dv1n)));       
    pDerivedVirtual1Print();   
    dv1n.deriveDVirtual_1_noData_print();

    cout << "ptr 所执行的值 = " << dec << *(int*)(*((int*)(&dv1n) + 1)) << endl;
    //cout << "分隔符的值 = " << dec << *((int*)(&dv1n) + 2) << endl;  //派生类没有数据就不需要分隔符了

    cout << "父类虚函数表中第二个slot的值也即虚函数Base::base_print()的入口地址 = " 
        << dec << *((int*)(*((int*)(&dv1n) + 2)) + 1) << endl;
    pFun pBasePrint = (pFun)(*((int*)(*((int*)(&dv1n) + 2)) + 1));      
    pBasePrint();   
    dv1n.base_print();
}

(3)Base的派生类DerivedVirtual_1没有自己的虚函数,有自己的成员数据,则DerivedVirtual_1的内存布局:(对这个不甚理解,但是Windows下在VS2012验证确实是这样!)
这里写图片描述
测试代码如下:

//虚继承中子类没有自己的虚函数,有新的成员数据
void DerivedVirtualInheritBase_noNewData()
{
    cout << "********************DeriveDVirtual_1_noData dv1n Model********************" << endl;
    DeriveDVirtual_1_noData dv1n;

    cout << *(int*)(*(int*)(&dv1n)) << endl;
    cout << "iDV_1的值 = " << dec << *((int*)(&dv1n) + 1) << endl;  

    cout << "父类虚函数表中第二个slot的值也即虚函数Base::base_print()的入口地址 = " 
        << hex << *((int*)(*((int*)(&dv1n) + 2)) + 1) << endl;
    pFun pBasePrint = (pFun)(*((int*)(*((int*)(&dv1n) + 2)) + 1));      
    pBasePrint();   
    dv1n.base_print();

    cout << "Base::iBase值 = " << dec << *((int*)(&dv1n) + 3) << endl;  
}

3、对象模型with多继承

再定义一个基类:
这里写图片描述

class Base_2
{
public:
    Base_2(int i);
    virtual ~Base_2(void);
    virtual void base_print() const;

protected:
    int iBase_2;
};

定义一个派生类继承自Base, Base_2
这里写图片描述

class DerivedMulti : public Base, public Base_2
{
public:
    DerivedMulti(int i);
    virtual ~DerivedMulti(void);

    virtual void base_print() const;  //重写父类的print
    virtual void derivedMulti_print() const; //自己的print

protected:
    int iDM;
};

则派生类DerivedMulti 及对象的布局方式如下图:
这里写图片描述
可以看出DerivedMulti对象中先放第一个基类Base的虚表指针和数据然后是第二个基类的虚表指针和数据,最后是子类的成员数据。子类的虚函数放在了第一个基类的虚表中,放在基类虚函数后面。基类在DerivedMulti对象中的存放顺序是按照声明时public Base, public Base_2顺序决定的。
模型验证:

void DerivedMultiInheritBases()
{
    cout << "********************DeriveDVirtual_1_noData dv1n Model********************" << endl;
    DerivedMulti dm(18);
    cout << "对象dm的内存地址 &dm = " << &dm << endl;
    cout << "对象dm中第一项 _vptr Base 虚表指针的值也即第一个基类虚表首地址 = " << hex << *(int*)(&dm) << endl;
    cout << "对象dm中第二项 int Base::iBase值 = " << dec << *((int*)(&dm) + 1) << endl;
    cout << "对象dm中第三项 _vptr Base_2 虚表指针的值也即第二个基类虚表首地址 = " << hex << *((int*)(&dm) + 2) << endl;
    cout << "对象dm中第四项 int Base_2::iBase_2值 = " << dec << *((int*)(&dm) + 3) << endl;
    cout << "对象dm中第四项 int DerivedMulti::iDM值 = " << dec << *((int*)(&dm) + 4) << endl;

    cout << "第一个基类Base的typeinfoPtr指针的存储位置 = " << hex << (*(int*)(&dm) - 1) << endl;
    cout << "第一个基类Base的typeinfoPtr指针的值也即Base's type_info的首地址 = " << hex << *((int*)(*(int*)(&dm) - 1)) << endl;

    cout << "第一个基类Base的虚函数表中第一个slot的值也即虚函数DerivedMulti::~DerivedMulti()的入口地址 = " << hex << *(int*)(*(int*)(&dm)) << endl;
    cout << "第一个基类Base的虚函数表中第二个slot的值也即虚函数DerivedMulti::base_print()的入口地址 = " << hex << *((int*)(*(int*)(&dm))+1) << endl;
    pFun pDerivedMultiBasePrint = (pFun)(*((int*)(*(int*)(&dm)) + 1));      
    pDerivedMultiBasePrint();   
    dm.base_print();
    cout << "第一个基类Base的虚函数表中第三个slot的值也即虚函数DerivedMulti::derivedMulti_print()的入口地址 = " << hex << *((int*)(*(int*)(&dm))+2) << endl;
    pFun pDerivedMultiPrint = (pFun)(*((int*)(*(int*)(&dm)) + 2));      
    pDerivedMultiPrint();   
    dm.derivedMulti_print();

    cout << "第二个基类Base_2的typeinfoPtr指针的存储位置 = " << hex << (*((int*)(&dm) + 2) - 1) << endl;
    cout << "第二个基类Base_2的typeinfoPtr指针的值也即Base_2's type_info的首地址 = " << hex << *((int*)(*((int*)(&dm) + 2) - 1)) << endl;

    cout << "第二个基类Base_2的虚函数表中第一个slot的值也即虚函数DerivedMulti::~DerivedMulti()的入口地址 = " << hex << *(int*)(*((int*)(&dm) + 2)) << endl;
    cout << "第二个基类Base_2的虚函数表中第二个slot的值也即虚函数DerivedMulti::base_print()的入口地址 = " << hex << *((int*)(*((int*)(&dm) + 2))+1) << endl;
    pFun pDerivedMultiBase2Print = (pFun)(*((int*)(*((int*)(&dm) + 2))+1));     
    pDerivedMultiBase2Print();   
    dm.base_print();
}

运行结果:
这里写图片描述

4、钻石型继承关系

这里写图片描述
DerivedVirtual_2类:

class DerivedVirtual_2 : virtual public Base
{
public:
    DerivedVirtual_2(int i);
    virtual ~DerivedVirtual_2(void);

    virtual void base_print() const;
    virtual void derivedVirtual_2_print() const;
protected:
    int iDV_2;
};

DeriveDVirtualDiamond类:

class DerivdeVirtualDiamond : public DerivedVirtual_1, public DerivedVirtual_2
{
public:
    DerivdeVirtualDiamond(int i);
    virtual ~DerivdeVirtualDiamond(void);

    virtual void base_print() const;
    virtual void derivedVirtual_1_print() const;
    virtual void derivedVirtualDiamond_print() const;

protected:
    int iDVD;
};

DeriveDVirtualDiamond类及对象内存布局方式:
这里写图片描述
如图所示先存放直接父基类DerivedVirtual_1的数据(先声明),然后是DerivedVirtual_2,再是DerivedVirtualDiamond本身的,然后使用分隔符0分割最后存放虚基类的。注意,DerivedDiamod::~DerivedVirtualDiamond(), DerivedDiamod::base_print()放在_vptr Base的虚表中。
验证模型:

void DerivedVirtualDiamondInherit()
{
    cout << "********************DerivedVirtualDiamond dvd Model********************" << endl;
    DerivdeVirtualDiamond dvd(21);
    cout << "对象dvd的内存地址 &dm = " << &dvd << endl;
    cout << "对象dvd中第一项 _vptr DerivedVirtual_1 虚表指针的值也即DerivedVirtual_1虚表首地址 = " << hex << *(int*)(&dvd) << endl;
    cout << "对象dvd中第二项 Ptr所指内存中的值 = " << dec << *(int*)(*((int*)(&dvd) + 1)) << endl;
    cout << "对象dvd中第三项 int DerivedVirtual_1::iDV_1的值 = " << dec << *((int*)(&dvd) + 2) << endl;

    cout << "对象dvd中第四项 _vptr DerivedVirtual_2 虚表指针的值也即DerivedVirtual_2虚表首地址 = " << hex << *((int*)(&dvd) +3)<< endl;
    cout << "对象dvd中第五项 Ptr所指内存中的值 = " << dec << *(int*)(*((int*)(&dvd) + 4)) << endl;
    cout << "对象dvd中第六项 int DerivedVirtual_2::iDV_2的值 = " << dec << *((int*)(&dvd) + 5) << endl;

    cout << "对象dvd中第七项 int DerivdeVirtualDiamond::iDVD的值 = " << dec << *((int*)(&dvd) + 6) << endl;

    cout << "对象dvd中第八项分隔符 0 的值 = " << dec << *((int*)(&dvd) + 7) << endl;

    cout << "对象dvd中第九项 _vptr Base 虚表指针的值也即Base虚表首地址 = " << hex << *((int*)(&dvd) + 8)<< endl;
    cout << "对象dvd中第十项 int Base::iBasee的值 = " << dec << *((int*)(&dvd) + 9)<< endl;

    cout << "基类DerivedVirtual_1的typeinfoPtr指针的存储位置 = " << hex << (*(int*)(&dvd) - 1) << endl;
    cout << "基类DerivedVirtual_1的typeinfoPtr指针的值也即DerivedVirtual_1's type_info的首地址 = " << hex << *((int*)(*(int*)(&dvd) - 1)) << endl;
    cout << "基类DerivedVirtual_1的虚函数表中第一个slot的值也即虚函数DerivdeVirtualDiamond::derivedVirtual_1_print()的入口地址 = " << hex << *(int*)(*(int*)(&dvd)) << endl;
    pFun pDerivedMultiVirtual1Print = (pFun)(*((int*)(*(int*)(&dvd))));     
    pDerivedMultiVirtual1Print();   
    dvd.derivedVirtual_1_print();
    cout << "基类DerivedVirtual_1的虚函数表中第二个slot的值也即虚函数DerivdeVirtualDiamond::derivedVirtualDiamond_print()的入口地址 = " << hex << *((int*)(*(int*)(&dvd))+1) << endl;
    pFun pDerivedMultiDiamondPrint = (pFun)(*((int*)(*(int*)(&dvd)) + 1));      
    pDerivedMultiDiamondPrint();   
    dvd.derivedVirtualDiamond_print();

    cout << "基类DerivedVirtual_2的typeinfoPtr指针的存储位置 = " << hex << ((int*)(*((int*)(&dvd) +3)) - 1) << endl;
    cout << "基类DerivedVirtual_2的typeinfoPtr指针的值也即DerivedVirtual_2's type_info的首地址 = " << hex << *((int*)(*((int*)(&dvd) +3)) - 1) << endl;
    cout << "基类DerivedVirtual_2的虚函数表中第一个slot的值也即虚函数DerivedVirtual_2::derivedVirtual_2_print()的入口地址 = " << hex << *(int*)(*((int*)(&dvd) + 3)) << endl;
    pFun pDerivedMultiVirtual2Print = (pFun)(*(int*)(*((int*)(&dvd) + 3)));     
    pDerivedMultiVirtual2Print();   
    dvd.derivedVirtual_2_print();

    cout << "基类Base的typeinfoPtr指针的存储位置 = " << hex << ((int*)(*((int*)(&dvd) + 8)) - 1) << endl;
    cout << "基类Base的typeinfoPtr指针的值也即Base's type_info的首地址 = " << hex << *((int*)(*((int*)(&dvd) + 8)) - 1) << endl;
    cout << "基类Base的虚函数表中第一个slot的值也即虚函数DerivdeVirtualDiamond::~DerivdeVirtualDiamond()的入口地址 = " << hex << *(int*)(*((int*)(&dvd) + 8)) << endl;
    cout << "基类Base的虚函数表中第二个slot的值也即虚函数DerivdeVirtualDiamond::base_print()的入口地址 = " << hex << *((int*)(*((int*)(&dvd) + 8)) + 1) << endl;
    pFun pDerivedBasePrint = (pFun)(*((int*)(*((int*)(&dvd) + 8)) + 1));        
    pDerivedBasePrint();   
    dvd.base_print();
}

运行结果:
这里写图片描述

参考:
[1] 《深度搜索C++对象模型》,候捷 译.
测试代码:http://download.youkuaiyun.com/detail/corcplusplusorjava/9172463

C++对象模型详解(整理) 一、何为C++对象模型? C++对象模型可以概括为以下2部分: 1. 语言中直接支持面向对象程序设计的部分 2. 对于各种支持的底层实现机制 语言中直接支持面向对象程序设计的部分,如构造函数、析构函数、虚函数、继承(单继承、多继承、虚继承)、多态等等第一部分这里我简单过一下,重点在底层实现机制。 阅读详情

相关推荐

gerrit 配置 ----Gitweb集成

此外,为了让用户能够查看任何关于 gitweb 的信息 项目中,用户必须能够阅读所有引用(包括 refs/meta/config,refs/meta/dashboards/*等)。或者,如果Gerrit通过反向代理送达,它可以 为gitweb的链接生成不同的URL(需要的) 在网页服务器上重写为)。Gerrit 代码审查可以管理和生成指向 gitweb 的超链接, 允许用户从Gerrit内容跳转到相同信息, 但由 gitweb 显示。默认为,这是 Linux 发行版上“gitweb”包的常见安装路径。

QuanQuanYuanXin的博客 1406

PCRNet: Point Cloud Registration Network using PointNet Encoding

PCRNet算是在ModelNet40这种object-centric数据集上用deep-learning做配准最好入手的一篇文章了,整体架构涉及简洁明了,一句话描述就是简单粗暴,话不多说,直接上网络架构图: Work Flow: 首先对输入source (N×3N \times 3N×3)与template (M×3M \times 3M×3)进行PointNet操作,即MLP + max-pooling后,分别得到描述各自点云的维度为1024的global feature vector:Φ(PS).

dloading7的博客 1648

<一>C++ 对象内存模型——继承

C++继承对象内存模型

EricShiOce的博客 651

Windows域账户无管理员权限也能装软件?3步搞定UAC设置(附安全提醒)

本文探讨了在Windows域环境中,如何通过配置用户账户控制(UAC)实现临时提权,让无管理员权限的域账户也能安装软件。文章详细介绍了三步走策略:临时加入本地管理员组、调整UAC行为以及至关重要的权限回收,并提供了脚本化部署方案与安全实践指南,旨在平衡企业IT管理的效率与安全。

fox11的博客 760

130、NPU的仿真测试:使用Gem5进行全系统仿真

这篇文章摘要(149字): 本文分享了使用Gem5全系统仿真器验证NPU设计的实践经验。作者通过搭建自定义NPU模型并集成到Gem5系统中,实现了CPU-NPU-内存协同仿真。关键点包括:1) 使用SimObject机制构建可配置的NPU模型;2) 合理配置IO和内存总线避免访问冲突;3) 开发Linux驱动实现CPU与NPU通信;4) 利用Gem5调试功能监控内部状态。相比RTL仿真和FPGA验证,该方法能快速验证微架构设计并定位系统级问题,有效减少了流片风险。文章特别强调了时钟域配置、总线仲裁、对齐访问

m0_50546716的博客 122

C++对象内存模型布局详解

C++封装带来的布局成本多大?由空类组成的继承层次中,每个类对象的大小是多大?在C++中有两种数据成员(class data members):static 和 nonstatic,以及三种类成员函数(class member function):static、nonstatic 和 virtual。

songbijian的博客 1663

LeRobot 入门教程(三)在仿真环境和自定义机器人上部署 LeRobot

本教程详细介绍了基于真实世界机器人和仿真环境的模仿学习实现方法,涵盖数据采集、训练策略和评估全流程。在真实机器人部分,重点讲解了远程操作设置、摄像头配置、数据集记录与管理技巧、策略训练(支持GPU/MPS设备)以及模型评估。仿真环境部分则演示了gym-hil的安装使用、模拟数据采集和策略训练。此外,教程还提供了自定义机器人集成指南,包括电机选择、接口继承、传感器/动作特征定义、连接管理和校准配置等核心步骤。所有方法均支持HuggingFace数据集中心和Weights & Biases工具链,为机器

2506_90492529的博客 2402

C++多重继承中的内存模型

C++多重继承中的内存模型C++语言通过引入虚函数表的形式来支持多态特性,并且为了解决多重继承中的冗余和二义性问题又引入了虚继承,这使得C++类的内存模型呈现出一定的复杂性。无虚函数时多重继承的内存模型C++要求所有实例化的对象都要有相应的内存地址,因此对一个不包含任何成员变量、成员函数的空类的实例会占用一个字节的内存空间。而非空类则按照以下规则安排其成员在内存中的排列顺序: 成员函数不占用内存空间

Mangost的专栏 3472

KCP(快速可靠UDP)——用带宽换延迟的极致艺术

想象你是一个外卖平台的调度员。这就是KCP的本质——

本博客聚焦游戏开发技巧、创业实战心得及大学热门课程干货。这里既有技术深度,也有思维广度,无论你是开发者、创业者还是高校学子,都能在这里找到适合自己的成长之路! 205

【Burp入门第二篇】Chrome配置BurpSuite代理教程

1、配置Burp代理为127.0.0.1:80802、扩展商店添加插件switchyomega,修改服务器和端口为下图:3、打开代理,选择刚才添加的代理

等风来 8368

CPP之对象模型

一、C++对象模型在c++中,class data members有两种:static、nonstatic;class member function有三种:static 、nonstatic、virtual;例如:下面这个class Pointclass Point { public: Point(float xval); virtual ~Point(); float x() con...

qq_32164245的博客 595

极智AI | 谈谈为什么卷积加速更喜欢 NHWC Layout

本文主要讨论一下为什么卷积加速更加喜欢 NHWC 的数据排布。

极智视界 2972

C++对象模型(整理)

C++对象模型 1、何为C++对象模型? C++对象模型可以概括为以下2部分: ① 语言中直接支持面向对象程序设计的部分 面向对象程序设计部分:如构造函数、析构函数、虚函数、继承(单继承、多继承、虚继承)、多态等。 ② 对于各种支持的底层实现机制 在C++类中有两种数据成员、三种成员函数: 两种数据成员:static、nonstatic。 三种成员函数:static、nonstatic、virtual。 代码举例说明: //base.h #pragma once #include<i

Aced96的博客 3928

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

深度探索C++对象模型之美

从概念来上来讲,每一个没有定义构造函数的类都会由编译器来合成一个默认构造函数,以使得可以定义一个该类的对象,但是默认构造函数是否真的会被合成,将视是否有需要而定。C++ standard 将合成的默认构造函数分为 trivial(不重要) 和 notrivial(重要) 两种,前文所述的四种情况对应于notrivial默认构造函数,其它情况都属于trivial。对于一个trivial默认构造函数,编译器的态度是,既然它全无用处,干脆就不合成它。

谢谢大家的关注 3494

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

详解 C++ 对象模型

文章目录何为 C++ 对象模型?基本 C++ 对象模型C++对象模型中加入单继承1. 无重写的单继承2. 有重写的单继承C++ 对象模型中加入多继承C++ 对象模型中加入虚继承1. 简单虚继承(无重复继承情况)2. 菱形继承(含重复继承、多继承情况)如何访问成员?多态如何实现? 何为 C++ 对象模型? C++ 对象模型可以概括为以下两个部分 语言中直接支持面向对象程序设计的部分 对于各种支持的底层实现机制 语言中直接支持面向程序设计的部分,如构造函数、析构函数、虚函数、继承(单继承,多继承,虚继承

qq_41033011的博客 3406

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

《深入探索C++对象模型》之一

前言:        由于经常使用C++,但是又对C++内部机制不是很深入了解,所以看了一些关于C++的经典书籍,其中有《C++ Primer》、《STL源码剖析》、《Efficitive C++》、《More Efficitive C++》。《C++ Primer》特别适合刚使用C++的程序员学习,里面详细介绍了一些C++基础知识,包括流和STL容器、适配器、算法还有C++的面向对象的一些知识。...

杨林峰的笔记 8912

c++对象模型

原文: https://www.cnblogs.com/HelloGreen/p/12641645.html 简单对象模型: 为所有成员建立一个Slot,也就是一个指针,让这个指针指向数据 在简单对象模型下,因为类的函数成员,数据成员都没有直接放到对象模型中(放入的是指向成员的指针),因此可以解决“因menbers不同因而需要不同的存储空间”(没明白书上这句话的意思)的问题。 优点:简单 缺点: 每增加一个成员,对象模型就得增加一个指针,内存与成员数量成线性关系 每次访问member,都需要

代码有点烂 279

深度探索C++对象模型

深度探索C++对象模型参考链接:第1章 关于对象C++的额外成本三种对象模型简单对象模型表格驱动对象模型C++对象模型class和struct关键字的差异三种编程典范一个类的对象的内存大小一些结论[^3] 参考链接: 深度探索C++对象模型 笔记汇总 第1章 关于对象 C++的额外成本 C++较之C的最大区别,无疑在于面向对象。类相较于C的struct不仅只包含了数据,同时还包括了对于数...

zzxiaozhao的博客 3752

C++的三种对象模型

在C++中,对象模型是指C++编译器如何实现对象的布局、内存分配、成员函数调用等机制。C++的对象模型主要有三种,分别是:简单对象模型、表格驱动对象模型、C++对象模型。

qq_55527904的博客 534

c++ 对象模型

title: object-mode-layout date: 2022/09/12 tags: c++对象模型 categories: c++对象模型

勤天下而拥家 1357

深度探索C++对象模型:三种对象模型

引言 现在有一个 Point 类,声明如下: class Point { public: Point(float xval); virtual ~Point(); float x() const; static int PointCount(); protected: virtual ostream& print(ostream &os) const; float _x; static int _point_count; }; 这个

qq_43351159的博客 558
上一篇: heap操作
下一篇: TCP/IP详解第二版(卷1 协议)
知行且执行
博客等级 码龄14年 38粉丝 73原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值