指针很简单。有一些存储器保存一个地址。要获取(有意义的)取消引用值,将返回地址指向的内存所包含的值。
引用以某种方式表达类似的东西:它们保持与临时对象的“链接”。但是当我分配或使用引用时会发生什么?
#include <iostream>
using namespace std;
int one(){
return 1;
}
int main()
{
const int &rone = one();
cout << &rone << endl;
return 0;
}
为什么这样做?这个地址是临时对象的地址吗?
sh-4.2# g++ -std=c++11 -o main *.cpp
sh-4.2# main
0x7fff9d2a4e74
这个地址指向哪里?如果它指向神奇的临时对象,为什么我不能做以下事情。 (我很清楚它是一个右值,&
只接受左值,但为什么?)
int main()
{
cout << &one() << endl;
return 0;
}
我的问题超出了c ++的编程方面。它更多地是在技术方面,C ++如何在内部工作。主要是我试图理解移动语义的上下文中的右值引用,这需要理解那些传统的引用(我几乎没用过)。
答案 0 :(得分:6)
另一种考虑引用的方法是别名。绑定引用为对象提供另一个名称。在C ++ 11之前,这两个片段完全相同:
int a = 5;
int &b = a;
与
int b = 5;
int &a = b;
此后,a
和b
都是同一对象的名称。 (C ++ 11引入了一种不对称性,decltype
将包含返回类型中的引用。
在你的第一个例子中没有任何不同。绑定rone
作为临时对象的名称,语言规则说这会使临时对象的生命周期也被扩展。
在你的第二个例子中没有引用。尽管运算符address-of具有相同的拼写,但它仍然是用于声明引用的符号的不同语言元素。这是非法的,因为语言定义表明address-of运算符不能应用于创建临时对象的表达式。
答案 1 :(得分:0)
您可以将临时绑定到rone
,因为编译器知道您不会修改它。读一个临时值是安全的,写它不是。
如果删除const
,代码将不再编译。
答案 2 :(得分:0)
变量(对象)和引用是“名称”(指针也是变量,所以同样适用)。程序员使用它们来识别程序中的某些对象。请注意,引用不仅引用临时对象。他们可能会引用任何有效的对象。
其他内容(即rvalues)没有名称,也没有指定的可访问“位置”它们的处理是编译器作业。在x86中的示例中(取决于调用约定),通常通过某些处理器寄存器返回小的返回值。
考虑:
int one() { return 1; }
int main()
{
one();
return 0;
}
此处的返回值消失了。 (如果可见,编译器甚至可能根本不会调用该函数。) 如果你可以获取返回值的地址,它必须存储在某个地方(可以使用有效地址访问的位置)。
因此,为了能够获取one()
返回的值的地址,必须从寄存器 EAX (当使用cdecl时通常驻留的位置)复制该值某个位置是“保存”,可由c ++程序(mer)访问。
如果您确实可以“直接”访问临时地址,会发生什么?
int const * p = &one(); // would this return "EAX"?
int c = *(p + 1); // ??²
你会在第二行阅读注册EBX吗?或者L1或L2中的任何内存位置或者??这样的事情没有意义。
c ++中唯一可访问的位置是堆栈内存和自由存储(“堆”),如果为返回类型创建变量或const引用,则使用其中一个。
int main()
{
int const a = one();
return 0;
}
来自第一个示例的事情变化不大:a
分配了名称和内存,而函数(静止)的返回值具有(并且始终不具有)。返回值被“计算”到EAX中,并通过将寄存器值复制到a
的内存位置来使用该整数初始化a
。
int const & r = one();
这里没什么区别。返回值存储在持久/可访问的内存位置,现在您可以使用ie
获取它的地址int const * p = &r;
这个地址指向哪里?
我不确定,但我不认为该标准规定了长时间临时的任何位置(如果我错了,请纠正我)。您需要知道的是:r
指的是有效对象,&r
是该对象的地址。 (您可以将地址与附近的一些堆栈变量地址进行比较,以确定它是否可能在堆栈中。)
r
和a
之间有一点不同:值(存储在r
所指的位置)不一定在堆栈上(虽然我认为它会最终在堆栈上),而上面的a
肯定在堆栈上。
当谈到物体时会出现另一个不同之处。 (至少在理论上,尽管允许c ++编译器以下列段落中a
和b
等效的方式进行优化。
struct X { /*stuff*/ };
X foo() { return X{ /*.args.*/ }; }
// ...
X const a = foo(); // A
X const & b = foo(); // B
在行B
中,b
是X
类型的常量对象的名称。 (在幕后,编译器会提前为对象选择一些位置并将foo
直接返回到该内存中,以避免复制它。)
在行A
中,a
将(在堆栈上)从foo
通过它的复制构造函数返回的临时值初始化。 (可以省略副本,优化编译器很可能直接将临时构造成。)
变量名称和引用是人类身份识别令牌。变量/对象是可能存在多个引用的单个实例。