c++ - C++:为什么对堆栈分配对象的引用与对堆分配对象的引用相同?
问题描述
StackOverflow 上的第一个问题,所以请放纵一下,不要犹豫,就如何更好地提出即将到来的问题给我反馈。谢谢 !
我想知道,
我知道对堆栈分配对象的引用不占用内存空间,并且在执行时没有开销,即:
int x;
x = 0;
和
int x;
int& y = x;
y = 0;
编译时应该完全相同。这对我来说听起来很明显,因为 b 的地址在编译时是已知的(因为它与 a 相同,在编译时已知)
但是,如果在编译时不知道地址,我不知道 C++ 编译器如何处理这个问题,当被引用的对象在堆上分配时会发生(据我所知),即:
A& x = *new A();
在这种情况下 &x 的值必须存储在编译代码中的某个位置,这会导致与在堆栈上分配对象的引用不同的行为,对吧?
因此,我知道对堆栈分配对象的引用地址必须是 r 值(因为我的第一个示例),因此无法修改它。
但是为什么不能修改对堆分配对象的引用的地址(因为据我所知它必须是左值,因为在编译时无法知道它)?为什么我们只有一个“引用”类型,而不是“对堆栈分配对象的引用”+“对堆分配对象的引用”类型,因为它们在实现中显然没有被以同样的方式对待?
感谢您的见解!
解决方案
在这种情况下的值
&x
必须存储在编译代码中的某个位置,这会导致与引用堆栈上分配的对象不同的行为,对吧?
引用如何在幕后工作取决于实现。每个编译器都可以自由地让它们按照他们想要的方式工作。但通常,最简单的解决方案是把它T&
当作一个T * const
永远不会存在nullptr
且永远不会被占用的地址。如果在编译时已知引用的对象,则可以优化引用,就像它从未存在过一样。但这也适用于指针。只是不可寻址的额外属性使引用更容易优化。
您可以确定的是,根据引用对象的创建方式(动态或作为本地对象) ,行为不会有所不同。是的,您展示的案例可能会导致编译器做不同的事情,但代码的行为不是编译器所做的事情的属性。这个推理是倒退的。中的代码根据语言标准定义了严格的行为。编译器所做的任何事情都必须保持该行为不变,只要它尊重标准所说的内容。所以引用有一个行为,一些行为的使用会触发不同的程序集生成,但结果会是一样的。
请注意,引用对指针有一些额外的要求,这使得与指针的比较有点不准确。作为一个例子看这个问题。
但是为什么不能修改对堆分配对象的引用的地址(因为据我所知它必须是左值,因为在编译时无法知道它)?
您无法更改引用所指内容的原因不是技术原因。根据设计,即使很容易实现该功能,您也不希望能够更改它所指的内容。这是因为引用的不可变特性被认为是一个优势。如果您想要一个保证包含地址并且您可以更改为引用其他内容的引用,那么该语言已经提供了可以为您执行此操作的指针。
为什么我们只有一个“引用”类型,而不是“对堆栈分配对象的引用”+“对堆分配对象的引用”类型,因为它们在实现中显然没有被以同样的方式对待?
同样,接口应该向开发人员公开这些差异并不是因为实现处理了功能的不同用例。如果您正在编写一个void mutate(int &)
函数,那么您希望以int
相同的方式处理所有 s,无论它们是如何创建的或编译器可能会进行哪些优化。对于仅适用于堆栈分配对象的函数,实际上并没有用例。
推荐阅读
- html - 如何根据html中的div大小调整图像
- python - 在 Mac 上的 Jupyter Notebook 和西班牙语 ISO 中将多行代码转换为注释
- dataframe - 将 Dataframe 写入 Azure SQL Server 表的最佳实践?
- r - 使用特定条件在 R 中拆分字符串
- c# - 以非升序从大文件中读取多行
- python - 如何将单端口用于 3 个容器而不是 3 个端口 Python-Flask、PostgreSQL 和 Angular8?所以我可以使用 Docker Run 而不是 Docker Compose
- c++ - IMG_Load() 中的 SDL_Texture 无法绘制?
- java - 采集界面
- java - H2 删除并创建别名 to_char 不起作用
- oracle - LAST_ANALYZE 在 dbms_stats.gather_table_stats 之后为空