C++中引用变量的内存是如何处理的?

3

我刚开始从基础学习C++,当我遇到引用变量时感到困惑。

据我所学,引用变量就像是别名(指向同一内存的另一个名称),所以在这种情况下,它不需要任何内存。

当我运行以下代码时:

class sample_class
{
    public:
        int num; //line1
        int& refnum = num; //line2
}

int main()
{
    sample_class sample_object;
    cout<< "sample_class object size : " << sizeof(sample_object) <<endl;
    return 0;
}

我得到的输出为:

sample_class对象大小:8

==>在这里,num的大小为4个字节(32位编译器),而refnum由于引用只是num的别名。那么为什么在这种情况下,对象的大小是8
==>此外,如果真的refnum像一个别名一样,那么这个信息(即refnum还保存/别名到num的同一内存地址的信息)何时被存储? 编辑: 考虑以下情况(更改sample_class的定义):
class sample_class
{
    public:
        char letter; //line3
        char& refletter = letter; //line4
        char letter_two; //line5
}

这里,如果我打印sample_class对象的大小,我得到的是12(尽管letterrefletterletter_two的大小都等于1)。但是如果我注释掉第4行,对象大小只有2这是怎么回事??? 我想从基础知识开始学习,所以如果我哪里错了,请纠正我

1
int& refnum = i; 中的 i 是否应该改为 num - wkl
抱歉...这是一个打字错误...已经修改了... - infinite loop
3个回答

8
一个引用是一个别名,它不应被视为一个新变量。你不能获得它的地址和大小。任何尝试这样做的操作都会取代获取别名对象的地址或大小。在实践中,大多数实现像指针一样实现它们,但标准并不要求这样做。它没有提到引用的预期大小。
来自:http://en.cppreference.com/w/cpp/language/reference 引用不是对象;它们不一定占用存储空间,虽然编译器可能会分配存储空间,如果需要实现所需的语义(例如,引用类型的非静态数据成员通常通过必要的存储内存地址增加类的大小)。
编辑:C++标准允许实现决定类型和类的大小以适应每个架构的独特要求。在这种情况下,在类的成员之间引入填充。C++中没有要求类的大小必须等于其成员大小的总和。有关此主题的更多信息,请参见cppreference.com上的对象和对齐
编辑2:关于sizeof(T&)仍然存在一些困惑。
来自http://en.cppreference.com/w/cpp/language/sizeof
当应用于引用类型时,结果是所引用类型的大小。
表达式sizeof(T&)被视为你写了sizeof(T)。这并不意味着T&的大小等于T的大小。这仅仅是说明你不能直接使用sizeof获取引用的大小。

@saikiran 这是一个不同的问题,有不同的答案,但Dr t的回答解决了最可能的情况:填充以确保最佳内存对齐。 - user4581301

2
除了已经提供的答案,我建议您阅读有关填充的材料,网址为:Data structure padding。这是一个有关C++类和结构体填充的基础讨论,包含一些简单的示例。请注意保留HTML标签,这是翻译的要求。

我已经查看了@Dr t提供的链接,但我在这里使用全部char,那么为什么对于每个char变量都需要填充呢?(请查看我的新代码-第3行、第4行和第5行),如果我注释掉第4行,为什么对象的大小会变成2 - infinite loop
1
@saikiran 你有 charchar&char。引用的内存布局要求不是由标准固定的,但它们可能与 char 不同。与包含三个 char(没有引用)的 struct 的大小进行比较。记住 sizeof(T&) 给出的是类型 T 的大小,而不是引用的大小。sizeof(char&) == 1 并不意味着 char& 的大小为 1,只是 char 的大小为 1。 - François Andrieux
@FrançoisAndrieux,我注意到了你说的话。但是,即使char&占用1字节,为什么编译器还要填充剩余的字节呢? 在int的情况下,情况是不同的,但是在这里为什么也要对剩余的普通char进行填充呢? - infinite loop
@FrançoisAndrieux 是的,我现在明白了,但我仍然不明白为什么编译器要填充那么多字节? - infinite loop
1
@saikiran 你很可能在x86平台上,其中引用的大小为4,并且具有4的对齐要求。由于你的成员按顺序存储,因此letter占用1个字节,填充了3个字节(因为引用只能从每4个字节开始),refletter占用4个字节,然后letter_two再占用1个字节。我不确定最后3个字节用于什么,但我猜测由于你的“结构体”的对齐要求为4,所以它被填充到4个字节的倍数。 - François Andrieux
显示剩余2条评论

1

引用存储其所引用的变量的地址(类似于具有更强的前/后置条件的指针)。 这意味着在32位体系结构上,sizeof(T&) == sizeof(T*) == 4

@FrançoisAndrieux关于T&实际大小的评论:

@nefas你说“这意味着在32位体系结构上,sizeof(T&) == sizeof(T*) == 4”,但事实并非如此。用一个更大的T尝试一下。例如,sizeof(std::array<int, 10>&)比指针大得多。您正在取T的大小,而不是T&

计算类/结构体大小时还要考虑结构体/类的填充:结构体/类的大小可能高于其成员大小的总和(我不会解释填充是如何工作的,因为我对此没有足够的了解)。


3
将引用实现为指针并不是标准要求,这仅仅是一种被广泛采用的实现选择。 - François Andrieux
1
这是在实现层面上,而不是在语言层面上! - Incomputable
我并没有说一个是用另一个实现的,而是说它们具有类似的语义。 - nefas
我之前不知道那个。我已经更新了我的答案,考虑到了你的评论。 - nefas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接