《深度探索c++内存模型》读书笔记 (一)

前言

在C语言里面,数据和函数是分开的,它们之间的关联是通过参数和返回值来取得联系。如:

typedef struct point3d
{
    
     
    float x;
    float y;
    float z;
} Point3d;

void print_point3d(const Point3d *p)
{
    
    
    printf("%g %g %g", p->x, p->y, p->z);
}

但是在c++中Point3d可以用 一种抽象的数据类型(abstract data type ADT)来实现

class Point3d
{   
public:
    Point3d(float x = 0.0, float y = 0.0, float z = 0.0):_x(x), _y(y), _z(z) {}
    // ... etc ...
private:
float _x;
float _y;
float _z;
};

从软件工程角度来看,使用ADT的数据封装比使用c语言中的函数+全局数据要好。但是,加上封装之后会有怎样的布局成本呢?

加上封装后的布局成本

  • 对于普通类来说,没有额外成本。
  • 对于含有virtual机制的类来说,主要有以下两种额外负担。
    1. virtual function 机制 用来支持 运行时绑定
    2. virtual base class 虚基类,用来支持同个类出现在多个继承关系中,有一个单一而被共享的实体(B、C继承A,D又继承B和C)。

c++对象模式

在c++中,成员函数有两种,为

  • static
  • nostatic
    成员函数有三种,分别为
  • static
  • nostatic
  • virtual

c++ 的内存模型:非静态数据成员被配置于每一个类对象中,静态数据成员放在所有的对象之外,静态和非静态函数成员也被放在所有的对象之外。虚函数则以以下两步获得支持:

  1. 每个类产生一堆指向虚函数的指针,放在一个表格中,这个表格被称为虚表(virtual table)。
  2. 每一个实例化 的对象,都被添加了一个指针,指向虚表。这个指针通常被称作vptr。这个vptr的设置(赋值)和重置通常由每一个类的构造函数,析构函数和拷贝运算符自动完成。每一个类的type_info (用来支持runtime type identification, RTTI) 也保存在虚表中,通常放在虚表的初始位置。

c++对象模型图

加上继承

c++ 支持单继承,多重继承,虚拟继承(virtual. 在此种情况下, 基类不管在继承链中被派生多少次,永远只会存在一个实体)
c++ 最初采用的继承模型并不运用任何间接性:基类对象数据成员直接被放在派生类对象中当中,自c++2.0(注意,不是c++20)起引入新导入的虚基类。有关内容将在后续讨论。

C++中的多态

c++中,对于对象的多态操作要求对象必须经由一个pointer或者reference来存取。例如:

derived_class *pc = new base_class();
derived_class &rc = base_class();

对象所占用的内存大小

一个实例化后的对象大小是多少呢?一般而言要有:

  • 非静态数据成员大小之和
  • 内存对齐(alignment)而填充的空间(可能在数据成员之间,也可能在末尾)。
  • 为了支持virtual 而产生的额外负担。

指针的类型

一个指向对象的指针和一个指向普通数据(int)的指针有什么不同呢?从内存的角度来说,没什么不同,他们都占据32位或者64位的空间。它们的差异,在于寻址出来的对象不同。也就是说,指针类型会教导百年一起如何解释一个特定内存区域的大小和内容

加上多态之后

在这里插入图片描述
下面的Bear指针和ZooAnimal指针有什么不同呢?

Bear b;
ZooAnimal *pz = &b;
Bear *pb = &b;

它们都指向Bear b 的第一个字节。差别是,pb所覆盖的地址保护整个b对象,而pz只包含b中ZooAnimal那一部分。

猜你喜欢

转载自blog.csdn.net/weixin_49265931/article/details/109124972