0
点赞
收藏
分享

微信扫一扫

C++对象模型的探索


类的对象空间

一个空类的对象占1个字节,因为对象需要内存,有内存就需要地址
只有一个char类,也是占一个字节,只有一个int32_t类,占4个字节
类的成员函数,不论是非静态还是静态都不占用类对象空间
一个类有一个虚函数,就会有一个指向这个虚函数的指针(vtbl),有两个虚函数,就会有两个指向虚函数的指针;用来存放这些指向虚函数的指针的表称为虚函数表(virtual table);同时这个虚函数表会保存在可执行文件中,在程序执行的时候载入到内存中
一个类中如果有虚函数,不论几个虚函数,类的对象都只会多4个字节,因为有虚函数,这个对象添加一个指针(vptr),指向类的虚函数表
内存对齐,是编译器对成员之间的内存占用做了调整,主要为了提高访问速度

this指针

子类对象是包含父类对象的,如果子类只有一个父类,那么这个子类对象的地址(this)和父类对象的地址(this)相同

如果子类继承多个父类,那么子类的对象地址和第一个父类对象地址相同

C++对象模型的探索_虚函数表


c 类继承a,b 类,所以c类对象的地址和a类对象的地址相同

调用哪个父类的成员函数,这个this指针就会被编译器自动调整到对象内存布局中,对应这个父类对象的起始地址

构造函数

如果我们没有定义默认构造或者拷贝构造或者赋值构造,编译器会在某些情况下,给我们自动生成对应的构造函数

默认构造函数

默认构造函数:没有参数的构造函数

一个类中定义的类 类型的对象的成员函数,且这个类是有构造函数的,才会被编译器默认生成构造函数,目的在于 要调用这个类 类型对象的构造函数

一个类有父类,并且这个父类有构造函数,才会被编译器默认生成构造函数,目的在于要调用父类的构造函数

一个类中有虚函数,会被编译器默认生成构造函数,目的在于把类的的虚函数的地址 给类对象的虚函数表指针

有虚基类,也会被编译器默认生成构造函数

虚基类 :一般是三层,爷爷Grand, 有两个爹 A,A2 ,有孙子C,

虚基类会有一个虚基类表,c对象中只会含有一份Grand对象空间

C++对象模型的探索_父类_02

类的默认构造函数做了哪些事情:
1、如果有虚函数,生成虚函数表,并把类的虚函数表地址赋给对象的虚函数表指针
2、调用父类的构造函数
3、初始化

拷贝构造函数

拷贝构造函数是构造一个新的对象,赋值构造函数是赋值一个已经存在的对象

class A
{
public:
int m_test;
};
class B
{
public:

A TestFunc(){
A test_a;
test_a.m_test = 19;
return test_a;// 先 生成一个临时A对象,然后调用拷贝构造函数,构造这个临时A 对象,然后析构test_a 对象,后再将这个临时对象返回
}
}

int main()
{
A test_A;
test_A.m_test = 17;
A mya2 = mya1; // 调用拷贝构造函数
}

编译器生成拷贝构造函数的条件与上述生成默认构造函数的条件类似

虚函数

虚函数表的指针在对象内存的起始位置还是末端位置,取决于编译器,一般是在开头的位置

一个类有虚函数就会有一个虚函数表,表中存放的是指向虚函数的指针,当实例化一个对象时,这个对象会产生一个指向这个虚函数表的指针; 当有多个同属于一个类的对象时,共享相同的虚函数表(vptr),指向虚函数表的地址是一样的

父类有虚函数表,子类肯定也会有虚函数表,并且一个只有一个虚函数表,在多重继承时子类中可能会有两个虚函数表

父类中虚函数,子类重写函数,无论是否加virtual 都是虚函数

如果子类没有虚函数,父类有函数,那么子类和父类的虚函数表内容是一样的,但是并不是一张表,是分别的两张表,在不同位置

C++对象模型的探索_父类_03


虚函数表指针是什么时候创建的?

编译器编译程序时会在相应的构造函数中添加为对象的虚函数表指针(vptr)赋值的代码,等在程序运行的时候,遇到创建对象的代码时,执行对应的构造函数,就可以给对象vptr 进行赋值了

虚函数表什么时候创建的?

编译器在编译期间就为每个有虚函数的类创建好了 虚函数表,不是在运行期间创建如下图:类A在栈上创建一个指针,在堆上new 一个A 对象,该对象的的虚函数表指针指向 只读数据段的虚函数表,虚函数表的存放的虚函数指针指向虚函数的代码

C++对象模型的探索_父类_04

多重继承虚函数表

一个对象,如果它的类有多个父类,则有多个虚函数表指针(注意是虚函数表指针,不是多个虚函数表)

例如:

多重继承,子类可能会有多个虚函数表,自己的虚函数会放在第一个父类的虚函数表中

C++对象模型的探索_虚函数_05


举报

相关推荐

0 条评论