派生类比基类大,尽管它由引用变量组成

Derived class is bigger than base class even though it consists of reference variables

本文关键字:引用 变量 基类 派生      更新时间:2023-10-16

从我读到目前为止,似乎引用变量不应该占用任何内存。相反,它们被视为它们引用的完全相同的变量,但具有另一个名称。

然而,当我运行下面的代码时,似乎并不总是这样:

#include <cstdio>
struct A
{
    int m[3];
};
struct B: A
{
    B():x(m[0]), y(m[1]), z(m[2]){}
    int& x;
    int& y;
    int& z;
};
int main(){
    printf("%u, %un", sizeof(A), sizeof(B));
    return 0;
}
输出:

12, 40

为什么B比A大那么多?

是否有其他方法可以访问,例如B.m[0]与B.x?

从我读到目前为止,似乎引用变量不应该占用任何内存。

你应该进一步阅读;)说真的,推荐信并不是魔法。所以在现实世界中,对象必须以某种方式存储有关引用绑定到的对象的信息。因此,虽然从概念上讲,引用没有大小,但它实际上非常像指针,通常编译器只使用指针。与指针不同的行为(非空,不可重赋值,不需要解引用)由编译器在编译时强制执行。

所以你看到的实际上是A的大小加上padding加上三个指针的大小。我猜你是在64位系统上,sizeof(int)是4,sizeof(void*)是8:

 12 bytes for the A subobject (4 for each int)
+ 4 bytes padding (to get to a multiple of 8 bytes)
+24 bytes for the 3 references/pointers in B (8 for each one)
--------------
 40 bytes total

对于您的另一个问题,给定类型为B的对象b,您可以直接访问b.m[0],因为它在A中是公共的并且是公共继承的。在不增加引用开销的情况下给它另一个名字是不可能的。

你理解错了。纯粹在局部使用的引用可以(而且经常被)被优化器消除。请注意,在它的生命周期内,我们仍然需要将它存储在某个地方(比如寄存器中)。

编译器不能像局部变量那样容易地预测对象的生命周期,因此,成员引用实际上不能被替换和丢弃:有必要保留一个指向实际内容的指针(4字节或8字节),以保持成员在对象生命周期内的一致性。

引用就像一个指针,不能为空,也不能被"重置"(使其指向与其最初指向的东西不同的东西)。因此,我们可能期望引用占用与指针相同的空间量(尽管这不能保证)。因此,三个int型,4个字节用于对齐,然后三个8字节指针将是40个字节,使您的示例非常合理。

您可以创建一个像B.x()这样的方法来返回一个值,而不占用更多的空间。但是由于c++缺乏"属性",你不能让B.x返回不占用空间的东西(即你不能让一些看起来像成员变量访问但行为像方法调用的东西)。