-1

我想计算一个对象使用的内存量(以字节为单位)。给定

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 函数(这里称为足迹)对吗?

4

7 回答 7

4

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

您还表达了对您的数组衰减为指针的担忧。情况并非如此sizeofsizeof(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)

于 2012-12-20T21:36:31.767 回答
1

由于该示例具有静态分配的数组,因此使用 sizeof(A) 将为您提供正确的大小。但是,有时由于位填充,大小会与您预期的不同。

但是,如果您有动态分配的数组,则需要执行以下操作:

lengthOfArray*sizeof(arrayElement);
于 2012-12-20T21:34:29.320 回答
1

不仅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%。[成员的确切布局取决于编译器、处理器架构、编译器开关等]。

于 2012-12-20T21:38:52.737 回答
1

在动态分配情况下,它变得更加复杂。实际说明您的班级使用情况的最佳解决方案是实现您自己的 operator::new(size_t size) [并可能调用 malloc 来分配 size 字节数],然后说明大小 - 但是,这不会说明分配器本身的开销,每次调用 new 大约 12 个字节。并且分配器还可以对正在分配的实际大小进行四舍五入。

于 2012-12-20T21:43:02.767 回答
1

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

您可以查看#pragma pack实现此类目标的声明。

于 2012-12-20T22:23:13.210 回答
1

我还没有看到提到的一个问题。当你这样做

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

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

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

当删除发生时,它需要知道在 new() 期间 size_ 是什么,因此它知道调用 std::string 析构函数的次数。C++ 有时可以在分配中添加几个字节来跟踪您分配了多少数组成员。

于 2012-12-20T22:33:15.617 回答
1

鉴于上述评论,我认为需要另一个答案。

我可以想到一些很好的解决方案来限制缓存使用的内存量: 1. 开始时,分配 X KB 内存(无论您的缓存“允许”使用什么)。根据需要将其切成部分。装满后,取出最旧的并重复使用(如果大小不同,可能需要多个)。

你需要一些方法来标记“最近使用”或“老化”的东西,这样你就可以扔掉最旧的东西。可能通过使用展开,当您获取某些东西时它会自动重新排序,因此最近的对象位于树的顶部。

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

  2. 如果您存储的对象的类型和大小不同,您可能会发现创建自己的堆并为您的类使用 operator::new() 是更好的选择(显然也是相应的 operator::delete())。关于如何制作自己的堆有多种解决方案(有时在 C++ 中称为免费存储)。我倾向于有一些圆形的东西,大小有限,这样你就不会得到如此糟糕的碎片。

于 2012-12-21T09:24:19.663 回答