C++ 面试:参考分配的实际用途(与参考传递相反)
有一次,我参加了一次面试,被问到通过引用分配变量的目的是什么(如下例):C++ 面试:参考分配的实际用途(与参考传递相反),c++,reference,variable-assignment,assignment-operator,assign,C++,Reference,Variable Assignment,Assignment Operator,Assign,有一次,我参加了一次面试,被问到通过引用分配变量的目的是什么(如下例): 我的答案是C++引用工作像C指针,但是不能假设空值,它们必须总是指向内存中的具体对象。当然,使用引用时的语法是不同的(不需要指针间接寻址操作符,对象属性将通过点(.)而不是箭头(->)操作符访问)。也许最重要的区别在于,与指针不同,指针可以指向不同的东西(即使在它与另一个指针指向同一个东西之后),如果一个引用被更新,引用也可以指向不同的东西,然后,指向同一对象的其他引用也会更新为指向同一对象 但是我接着说,上面引用的用法是
<>我的答案是C++引用工作像C指针,但是不能假设空值,它们必须总是指向内存中的具体对象。当然,使用引用时的语法是不同的(不需要指针间接寻址操作符,对象属性将通过点(.)而不是箭头(->)操作符访问)。也许最重要的区别在于,与指针不同,指针可以指向不同的东西(即使在它与另一个指针指向同一个东西之后),如果一个引用被更新,引用也可以指向不同的东西,然后,指向同一对象的其他引用也会更新为指向同一对象 但是我接着说,上面引用的用法是非常无用的(也许这就是我出错的地方),因为我看不到通过引用赋值的实际优势:因为两个引用都指向同一个东西,你可以很容易地使用一个引用,我想不出有哪一种情况不是这样的。我接着解释了引用作为传递引用函数参数是有用的,但在赋值中不是。但面试官说,他们总是在代码中通过引用进行分配,结果我不及格(然后我继续为一家公司工作,该公司是该公司的客户,但这不是重点)
不管怎么说,几年后,我想知道我哪里出了问题。首先,为了那家公司的缘故,我希望这不是他们不雇佣你的唯一原因,因为这只是一个小细节(不,你不知道公司为什么不雇佣你) 正如在评论中所提到的,引用在其生命周期内永远不会改变所引用的内容。一旦设置,引用将引用相同的位置,直到它“消失” 现在,引用对于简化表达式非常有用。假设我们有一个包含大量复杂内容的类或结构。说这样的话:
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 ...
}
}
上面的代码假设do stuff
实际上以某种方式对cur
元素进行了排序,如果不是,您可能应该使用const A&cur=…
我经常使用这种技术使它更清晰,重复性更小。在这种特殊情况下,将引用分配给同一范围内的基元类型的局部变量,这种分配是非常无用的:使用
j
没有什么比使用I
做不到的。它也有一些轻微的负面后果,因为可读性会受到影响,优化器可能会感到困惑
以下是分配引用的一种合法用法:
void C::func()
{
...
A &cur = list[i]->arr[j];
if (cur.x == 4 && cur.y == 5 &&
(cur.z < 10 || cur.z > 90))
{
... do stuff ...
}
}
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;
}
};
类演示{
私人:
地图缓存;
字符串读取资源(int id){
字符串资源\ u字符串;
…//获取非空资源字符串的漫长过程
返回资源字符串;
}
公众:
字符串和按id获取(int id){
//这是一个好把戏
string&res=cache[id];
如果(分辨率大小()==0){
//指定res将修改映射中的字符串
res=读取资源(id);
}
返回res;
}
};
如上所述,引用类型的变量
res
指的是检索或新建的映射元素。如果字符串是新创建的,则代码调用“real”getter,并将其结果分配给res
。这也会自动更新缓存,为我们在缓存
映射中保存另一个查找。“如果一个引用被更新,那么指向相同对象的其他引用也会被更新,以指向非常相同的对象。”我想知道你从哪里得到的。好的,因此,编译器可以自行执行的类型和编译器优化就少了一点。因此,基本上,它只对避免编写长串属性链和数组元素访问有用。谢谢。@JohnSonderson这不仅仅是“少打字”,而是:替换res=read\u资源(id)代码>具有缓存[id]=读取资源(id)
避免在映射图上进行第二次日志(N)
查找,这一点很重要。是的,这是真的,但我对编译器优化的评论是指编译后的代码并不总是与您键入的代码相对应。如果编译器发现cache[id]
多次被引用,它将优化代码并重写代码,以便创建对cache[id]
的引用,并用该引用替换其他cache[id]
变量。然后编译代码,产生相同的结果。@JohnSonderson编译器不允许优化out函数调用。试想一下:如果同时访问映射,并且在两次访问之间对映射进行了更改,该怎么办?编译器没有办法知道它;程序员是这样做的。因此,手动添加引用是避免第二次函数调用的唯一方法。我猜编译器无法确定一段代码是否作为多线程解决方案的一部分使用,C++11现在有一个
包含文件和相关函数调用。感谢您指出这一点。指针也可以这样做,只是使用引用可以少键入几个字符,因此在这些情况下,引用更可取。好吧,至少我很满意我在面试中没有错过太多。是的,当然,你几乎总是可以对指针和引用做同样的事情。有些事情无法使用引用来完成(例如,将引用重新指定为指向其他对象)。
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;
}
};