如何计算c++中类对象使用的内存量?

How do I calculate the amount of memory a class object uses in C++?

本文关键字:对象 内存 何计算 计算 c++      更新时间:2023-10-16

我想以字节为单位计算对象使用的内存量。鉴于

struct A
{
    float data[16];
    int index;
};
struct B
{
    A a;
};

下面的方法是正确的吗?

template <class Type>
size_t footprint();
template <>
size_t footprint<A>()
{
    return sizeof(float) * 16 + sizeof(int);
}
template <>
size_t footprint<B>()
{
    return footprint<A>();
}

我不确定关于footprint(),因为我听说编译器可能会添加额外的信息只是为了存储成员变量,我不确定关于footprint(),因为它引用一个类对象。这也需要一些内存吗?

编辑:好的,假设情况发生了变化,我们不再使用静态数组,而是使用实际指针:
#include <iostream>
using namespace std;
struct A
{
    A(int size_)
    {
        data = new float[size_];
        size = size_;
    }
    ~A()
    {
        delete [] data;
    }
    float* data;
    int size;
};
struct B
{
    B() : a(16) {}
    A a;
};
size_t footprint(const A& a)
{
    return sizeof(float) * a.size + sizeof(int);
}
size_t footprint(const B& b)
{
    return footprint(b.a);
}
int main()
{
    A a(16);
    B b;
    cout << "sizeof(A) = " << sizeof(A) << endl;
    cout << "sizeof(B) = " << sizeof(B) << endl;
    cout << "footprint(a) = " << footprint(a) << endl;
    cout << "footprint(b) = " << footprint(b) << endl;
}

这里你需要一个特殊的sizeof函数(这里叫做footprint),对吧?

正确的方法是使用sizeof(A)sizeof(B)。将成员的大小相加是不正确的,因为编译器可以自由地放入额外的空间,称为填充,以正确对齐。sizeof占这个填充。

您还表达了对数组衰变成指针的担忧。这与sizeof不同。sizeof(an array)将给出该数组占用的总字节数,前提是它仍然是数组形式并且没有衰减为指针。事实是,标准明确禁止在sizeof中发生这种衰减,所以您在那里是安全的:

c++ 11 N3485§5.3.3/4:

左值到右值的标准转换(4.1)、数组到指针的标准转换(4.2)和函数到指针的标准转换(4.3)则不是适用于sizeof.

的操作数

sizeof(A) == 16 * sizeof(float) + sizeof(int) + sizeof(additional padding)

因为这个例子有一个静态分配的数组,那么使用sizeof(a)会给你正确的大小。但是,由于位填充的原因,有时大小会与您期望的不同。

然而,如果你有动态分配的数组,你需要这样做:

lengthOfArray*sizeof(arrayElement);

不仅是return sizeof(float) * 16 + sizeof(int);相当麻烦的更复杂的对象,但也可能是不正确的。C语言规范允许编译器"填充"结构。例如,我们有

class X {
public:
    int i;
    char c;
    double d;
};

你调整结构大小的方法会说它占用4 + 1 + 8(或类似的),但实际的sizeof(X)会给4 + 4 + 8。在小型和大型数据类型极端混合的情况下,我们很容易看到50-75%的误差。[成员的确切布局取决于编译器、处理器架构、编译器开关等]。

在动态分配情况下,情况变得复杂得多。实际考虑类使用情况的最佳解决方案是实现自己的操作符::new(size_t size)[并可能调用malloc来分配size的字节数],然后考虑大小-然而,这不会考虑分配器本身的开销,每次调用new都会增加12个字节以上的开销。分配器也可以将实际分配的大小四舍五入。

作为前面答案的补充,编译器为您提供了控制填充行为的方法(因此您可以对sizeof(A)产生影响,如果内存占用在程序的那个点对您特别重要,如果您想通过网络发送您的结构,或其他)。

您可以查看#pragma pack语句以实现此目标。

我还没有看到一个问题被提到。当你做

float * data = new float[size_];
delete [] data;

则sizeof(float) * size_是分配的内存量。但是当你这样做

std::string * data = new std::string[size_];
delete [] data;

在执行delete操作时,需要知道new()操作期间size_的值,以便知道调用std::string析构函数的次数。c++有时可以在分配中添加几个字节,以跟踪您分配了多少数组成员。

根据上面的评论,我认为需要另一个答案。

我可以想到一些很好的解决方案来限制缓存使用的内存量:1. 每天开始时,分配X KB的内存(无论您的缓存"允许"使用多少)。根据需要把它切成几段。满了之后,取出最旧的并重复使用(如果它们的大小不同,可能需要多个)。

你需要一些方法来标记"最近使用的"或"旧的",这样你就可以扔掉最旧的东西。可能是通过使用一个自动重新排序当你获取一些东西,最近的对象在树的顶部。

  1. 与上面类似,但是使用固定数量的固定大小的数据块-例如16或32(或256,或500或任何对典型缓存条目大小最有意义的东西)。在一天开始时,将所有缓存条目放入"空闲"容器(例如列表)。当您需要一个新的缓存条目时,获取空闲列表的顶部。如果列表为空,则查找最老的列表并将其重用到缓存中。

  2. 如果您存储的对象类型和大小不同,您可能会发现创建自己的堆,并为您的类使用operator::new()是更好的选择(显然还有相应的operator::delete())。对于如何创建自己的堆(在c++中有时称为自由存储),有多种解决方案。我更倾向于用有限尺寸的子弹,这样就不会有这么严重的碎片。