1.C++对象模型
nonstatic data members
被配置于每一个class object
内.
static data members
被存储在class object
外.
static
和nonstatic function members
实现代码也存储在class object
外.
对virtual functions
:
a. 每个class
产生一堆指向virtual functions
指针,放在表格中.表中每个条目指向该类型的一个虚函数代码实例的起始地址.
b. 每个class object
被安插一个指针,执行相关的virtual table
.vptr
的设定,重置由每一个class
的constructor/destructor/copy assignment
自动完成.每一个class
所关联的type_info object
也放在表格中.
c. 一个class object
所需内存构成,一般要有:
c.1. 其nonstatic data members
的总和大小
c.2. 加上由alignment
的需求而填补上去的空间
c.3. 加上为支持virtual
而产生的负担
2.注意点
对一个包含虚表指针的类对象执行memset(&obj, 0, sizeof(obj))
存在把保存在类对象内的虚表指针设为0
的风险.
3.虚拟继承下的类型尺寸
class X {
};
class V1{
int i2;
virtual void fun(){}
};
class V2:public virtual V1{
int i;
virtual void fun2(){}
};
class V3:public V2,public virtual V1
{
public:
int i;
virtual void fun3(){}
};
类型尺寸:
a. sizeof(X)
为1
.
b. sizeof(V1)
为16
.
c. sizeof(V2)
为32
.
d. sizeof(V3)
为32
.
在C++
中,当使用虚继承时,类的内存布局会发生变化,主要是为了处理多重继承时的公共基类子对象问题。虚继承通过在继承体系中引入一个共享的、指向公共基类子对象的指针(通常称为虚基类指针或vptr
),来确保只有一个公共基类的实例存在于派生类中。
针对上述:
(1). class X {}
: X
是一个空类,但即使如此,编译器通常也会为它分配至少一个字节的内存,以区分不同的对象。这是为了确保两个不同类型的空对象在内存中占据不同的地址。若空类作为基类时,其尺寸计算为0
.因为此时派生类非空类时,类型实例至少有1
个字节内存.无需上述处理.派生类也是空类时,派生类实例整体占1
字节即可.
(2). 虚拟继承
上述虚拟继承下V1,V2,V3
实例对象可能的内存布局:
上述是V1
.
上述是V2
.
上述是V3
.
4.C++
如何实现多态
对含虚函数的class
有且仅有一个virtual table
,每个table
内含其对应之class object
中所有active virtual functions
函数实例的地址.
active virtual functions
包括:
(1). class
自身新定义或重载的虚函数.
(2). 继承自base class
的虚函数.
一个实际例子
class point{
public:
virtual ~point(){}
virtual void mult() = 0;
virtual void y()[}
virtual void z(){}
private:
int x;
};
x
实例对象内存可能的布局示意图:
指向的虚表可能的构成示意:
class point2d : public point
{
public:
virtual ~point2d(){}
virtual void y(){}
virtual void mult(){}
public:
int y;
};
一个point2d
实例可能的内存布局示意:
point2d
可能的虚表构成:
在上述背景下,通过基类point
指针ptr
调y()
时:
(1). 依据实例对象起始地址,容易定位到对象内虚表指针.
(2). 通过函数名易于定位到需表中表项位置.
这样就完成了多态调用.
5.多重继承下多态的一个可能实现说明
struct Base1{
public:
int a;
virtual ~Base1(){}
virtual SpeakClearly(){}
};
struct Base2{
public:
int b;
virtual ~Base2(){}
virtual void mumble(){}
virtual void clone(){}
};
内存布局略.
struct Derived : public Base1, public Base2{
public:
int c;
virtual ~Derived(){}
virtual void clone(){}
};
这里一个Derived
实例对象将含两个虚表指针.(单独继承下只有一个).且每个Base1
部分的虚表指针,Base2
部分的虚表指针所指向的虚表.由基类虚函数,派生类重载综合得到.