仅功能对象的内存结构?

时间:2010-07-30 15:32:53

标签: c++

假设我们有一个看起来像这样的课程:

class A 
{ 
    public:
        int FuncA( int x );
        int FuncB( int y );

        int a;
        int b;
};

现在,我知道这个类的对象将只用两个ints布局在内存中。也就是说,如果我创建了类A的实例的向量,则一个实例将有两个ints,然后对于第二个实例将有两个ints等。对象是POD

但是让我们说这堂课看起来像这样:

class B
{ 
    public:
        int FuncA( int x );
        int FuncB( int y );
};

这个类的对象在内存中看起来是什么样的?如果我用B的实例填充向量...向量中有什么?我被告知非虚拟成员函数最终被编译为自由函数,这些函数与声明它们的类的实例完全无关(虚函数也是,但是对象存储带有函数指针的vtable) 。访问限制仅仅是语义上的“人”级别。只有类的数据成员(和vtable等)实际上构成了对象的内存结构。

再说一次,类B的对象在内存中是什么样的?它是某种占位符价值吗?必须有东西,我可以拿对象的地址。它必须指向某事。无论是什么,编译器是否允许内联/优化这些对象并将方法调用视为正常的自由函数调用?如果我创建这些的向量并在每个对象上调用相同的方法,编译器是否可以消除向量并用一堆普通的调用替换它?

我只是好奇。

7 个答案:

答案 0 :(得分:7)

C ++中的所有对象都保证sizeof> = 1,以便每个对象都有一个唯一的地址。

我没有尝试过,但我想在你的例子中,编译器会为数组/向量中的每个函数对象分配但不初始化1个字节。

答案 1 :(得分:6)

正如Ferruccio所说,C ++中的所有对象都保证大小至少为1.大多数情况下,它是1个字节,但填写了对齐的大小,但无论如何。

但是,当用作基类时,它不需要填充任何空格,因此:

class A  {} a;    // a is 1 byte.
class B  {} b;    // b is 1 byte.
class C  { A a; B b;} c; // c is 2 bytes.
class D : public A, B { } d;  // d is 1 byte.
class E : public A, B { char ee; } e;  // e is only 1 byte

答案 2 :(得分:4)

  

这个类的对象在内存中看起来像什么?

这完全取决于编译器。空类的实例必须具有非零大小,以便不同的对象具有不同的地址(除非它被实例化为另一个类的基类,在这种情况下它根本不占用任何空间)。通常,它将由一个未初始化的字节组成。

  

无论是什么,编译器是否允许内联/优化这些对象并将方法调用视为正常的自由函数调用?

是;编译器不必创建对象,除非您执行类似于获取其地址的操作。标准库中使用了很多空函数对象,因此重要的是它们不会引入任何不必要的开销。

答案 3 :(得分:2)

我进行了以下实验:

#include <iostream>

class B
{ 
    public:
        int FuncA( int x );
        int FuncB( int y );
};

int main()
{ 
    std::cout << sizeof( B ) ;
}

结果是1(VC ++ 2010)

在我看来,该类实际上不需要任何内存,但是对象不能为零大小,因为如果你以其地址为例,这将没有语义意义。 Ferruccio的答案证实了这一点。

答案 4 :(得分:0)

我从现在开始说的所有内容都依赖于实现 - 但大多数实现都符合。

如果类有任何虚方法,则会有一个不可见的vtable指针成员。但是,你的例子并非如此。

是的,编译器会将成员函数调用视为与自由函数调用相同,除非它是虚函数。即使它是一个虚函数,如果编译器在调用时知道具体类型,它也可以绕过vtable。每个调用仍然依赖于对象,因为有一个不可见的this参数,对象的指针会被添加到调用中。

答案 5 :(得分:0)

我认为它们看起来就像C ++中的任何对象:

  • 该类的每个实例占用空间。因为C ++中的对象必须具有至少1的大小(因此它们具有唯一的地址,如Ferruccino所说),未指定任何数据的对象不会受到特殊处理。
  • 非虚拟函数在类中根本不占用任何空间。相反,它们可以被认为是这样的函数:

    int B_FuncA(B *this, int x);
    int B_FuncB(B *this, int y);
    

如果其他.cpp文件可以使用此类,我认为这些将成为实际的类实例,而不是常规函数。

如果您只希望自己的函数是免费的而不是绑定到对象,则可以make them staticuse a namespace

答案 6 :(得分:0)

  

我被告知非虚拟成员函数最终编译为自由函数,与声明它们的类的实例完全无关(虚函数也是如此,但对象存储vtable与函数指针)。访问限制仅仅是语义上的“人”级别。只有类的数据成员(和vtable等)实际上构成了对象的内存结构。

是的,这通常是它的工作原理。可能值得指出标准中未指定的区别,并且它不是 required - 在编译器中实现这样的类是有意义的。

  

再说一次,B类对象在内存中是什么样的?它是某种占位符价值吗?必须有东西,我可以拿对象的地址

完全。 :) C ++标准要求对象占用至少一个字节,正如您所说的那样。它必须有一个地址,如果我把这些对象放到一个数组中,我必须能够增加一个指针以获得“下一个”对象,所以每个对象必须有一个唯一的地址并占用至少1个字节。 (当然,空对象 不能正好占用1个字节。出于性能原因,一些编译器可能会选择将它们设为4个字节或任何其他大小)

合理的编译器甚至不会使它成为占位符值。为什么要在这个字节中写入任何特定值呢?我们可以让它包含创建对象时所持有的任何垃圾。它永远不会被访问。只分配一个字节,从不读取或写入。