数据成员

类的空间占用

  • 原书内容与现实标准略有不符,本节按照现实标准来

  • 本节使用 attributepacked 关闭内存对齐

  • 本节编译的代码为64位代码

  • 本节使用的编译器为 GCC 9.30

考虑以下代码:

class X{
}__attribute__((packed));

class Y : virtual public X{

}__attribute__((packed));

class Z : virtual public X{

}__attribute__((packed));

class A: public Y, public Z{

}__attribute__((packed));

int main() {
    cout<<sizeof(X)<<" "<<sizeof(Y)<<" "<<sizeof(Z)<<" "<<sizeof(A)<<endl;
    return 0;
}

输出结果为:

1 8 8 16

需要注意的是上text大小则与下列因素有关:

  1. 支持虚有基类造成的负担。该负担反应在编译器插入的指针上,此指针要么指向虚函数的子对象,要么指向某个相关表格。

  2. 空虚类优化。编译器对空的虚有基类做出优化,使空虚有基类占用的 1 个字节不会表现在其子类中。

  3. 内存对齐。

本文中已经关闭内存对齐

因此,Y, Z 的大小与上述指针(该指针为 8 个字节)和编译器执行的优化处理有关(否则 Y, Z 就是 9 个字节了)。

由此可得:

sizeof(Y) = sizeof(X) + sizeof(指针) + sizeof(内存对齐) 但是此时由于编译器对 sizeof(X) 进行优化,使其大小为零,而本节中又关闭了内存对齐。

数据成员的绑定

该节说明的问题在现代 C++ 已经解决

考虑以下代码:

extern int x;
typedef int length;

class X{
public:
    int getX(){
        return x; // 此处将绑定到全局变量
    }
private:
    typedef float length; // 此处将报错
    int x;
};

以上问题出现在旧式C++中,为了解决这个第一个问题,编译器将对成员函数的数据绑定推迟到类定义之后:

extern int x;
typedef int length;

class X{
public:
    int getX(){
        return x; // 此处将绑定到全局变量
    }
private:
    typedef float length; // 此处将报错
    int x;
};
// 在此处为成员函数进行数据成员

而第二个问题则依赖于 防御性编程

  • 所有的数据成员生命放置在类的开头处,以保证正确的绑定

  • 所有的嵌套声明放置在类的开头

  • 所有的类成员函数与其实现分离

数据成员的内存排列

  • 下文中将使用“权限区”代指 C++ public、 protected、public等部分

  • 对于现代编译器而言,多个相同的权限区将会被合成为一个

C++标准对数据成员有一下规定:

  • 同一权限区内的数据成员排列顺序与声明顺序相同(不一定连续)

  • 由编译器生成的数据成员(如 vptr)可以排列在任意位置

  • 不同权限区的数据成员顺序不做规定

此处静态成员不考虑在内

数据成员的存取效率

  • 静态成员的存取没有任何空间或效率的代价。
    • 因为其被储存在程序的数据段,通过 object.static_data 的方式只是一种便利手段。

    • 若多个类声明了相同名称的静态数据成员,则编译器会对其命名进行修饰

  • 对数据成员的存取必定经过 this,其方式是 this + offset(data_member)

Last moify: 2024-11-22 02:16:03
Build time:2025-07-18 09:41:42
Powered By asphinx