提问人:John Sonderson 提问时间:2/8/2015 更新时间:2/8/2015 访问量:94
访谈:按参考分配(而不是按参考传递)的实际用途
Interview: practical uses of assign-by-reference (as opposed to pass-by-reference)
问:
有一次,我参加了一次面试,被问到通过引用分配变量的目的是什么(如下例所示):
int i = 0;
int &j = i;
我的回答是 C++ 引用的工作方式类似于 C 指针,但不能假设 NULL 值,它们必须始终指向内存中的具体对象。当然,使用引用时的语法是不同的(不需要指针间接运算符,并且对象属性将通过点 (.) 而不是箭头 (->) 运算符访问)。也许最重要的区别是,与指针不同,在指针中,你可以使指针指向不同的事物(即使它与另一个指针指向同一事物),而使用引用,如果一个引用被更新,那么指向同一事物的其他引用也会更新为指向同一个对象。
但后来我继续说,上面引用的使用是相当无用的(也许这就是我出错的地方),因为我看不到按引用分配的实际优势:由于两个引用最终都指向同一件事,你可以很容易地用一个引用来做,并且想不出这种情况的情况。我接着解释说,引用作为逐个引用函数参数很有用,但在赋值中却没有用。但面试官说他们总是在代码中引用分配,并且让我不屑一顾(然后我继续为这家公司的客户工作,但这不是重点)。
无论如何,几年后,我想知道我可能在哪里出错了。
答:
首先,我希望看在那家公司的份上,这不是他们不雇用你的唯一原因,因为这是一个微不足道的细节(不,你真的不知道为什么一家公司不雇用你)。
正如评论中提到的,参考文献在其一生中永远不会改变它们所指的内容。一旦设置,引用将引用相同的位置,直到它“死亡”。
现在,引用对于简化表达式非常有用。假设我们有一个包含大量复杂内容的类或结构。这样说:
struct A
{
int x, y, z;
};
struct B
{
A arr[100];
};
class C
{
public:
void func();
B* list[20];
};
void C::func()
{
...
if (list[i]->arr[j].x == 4 && list[i]->arr[j].y == 5 &&
(list[i]->arr[j].z < 10 || list[i]->arr[j].z > 90))
{
... do stuff ...
}
}
那里有很多重复。因此,我们可以使用引用重写它:list[i]->arr[j]
void C::func()
{
...
A &cur = list[i]->arr[j];
if (cur.x == 4 && cur.y == 5 &&
(cur.z < 10 || cur.z > 90))
{
... do stuff ...
}
}
上面的代码假设实际上是以某种方式对元素进行修改,如果没有,您可能应该改用。do stuff
cur
const A &cur =...
我经常使用这种技术来使其更清晰,重复更少。
评论
在将引用分配给同一作用域中基元类型的局部变量的这种特殊情况下,赋值是非常无用的:没有任何事情是你不能使用的。它也有一些轻微的负面影响,因为可读性会受到影响,优化器可能会感到困惑。j
i
以下是分配引用的一种合法用途:
class demo {
private:
map<int,string> cache;
string read_resource(int id) {
string resource_string;
... // Lengthy process for getting a non-empty resource string
return resource_string;
}
public:
string& get_by_id(int id) {
// Here is a nice trick
string &res = cache[id];
if (res.size() == 0) {
// Assigning res modifies the string in the map
res = read_resource(id);
}
return res;
}
};
上面,引用类型的变量是指映射中检索或新创建的元素。如果字符串是新创建的,则代码将调用“实际”getter 并将其结果分配给 。这也会自动更新缓存,从而节省我们在地图中的另一次查找。res
res
cache
评论
res = read_resource(id);
cache[id] = read_resource(id);
Log(N)
map
cache[id]
cache[id]
cache[id]
<thread>
评论