我理解与任何其他变量一样,参数的类型决定了参数与其参数之间的交互。我的问题是,为什么要引用参数而不是为什么不引用参数的原因是什么?为什么有些函数参数是引用而有些不是?无法理解这样做的好处,有人可以解释一下吗?
4 回答
存在通过引用传递的能力有两个原因:
- 修改函数参数的值
- 为了避免出于性能原因复制对象
修改参数的示例
void get5and6(int *f, int *s) // using pointers
{
*f = 5;
*s = 6;
}
这可以用作:
int f = 0, s = 0;
get5and6(&f,&s); // f & s will now be 5 & 6
或者
void get5and6(int &f, int &s) // using references
{
f = 5;
s = 6;
}
这可以用作:
int f = 0, s = 0;
get5and6(f,s); // f & s will now be 5 & 6
当我们通过引用传递时,我们传递变量的地址。通过引用传递类似于传递指针——在这两种情况下都只传递地址。
例如:
void SaveGame(GameState& gameState)
{
gameState.update();
gameState.saveToFile("save.sav");
}
GameState gs;
SaveGame(gs)
或者
void SaveGame(GameState* gameState)
{
gameState->update();
gameState->saveToFile("save.sav");
}
GameState gs;
SaveGame(&gs);
由于只传递了地址,因此不需要复制变量的值(对于大型对象来说可能非常大) 。因此,通过引用传递可以提高性能,尤其是在以下情况下:
- 传递给函数的对象很大(我会在这里使用指针变体,以便调用者知道函数可能会修改变量的值)
- 该函数可以被多次调用(例如在循环中)
另外,请阅读const
参考资料。使用时,不能在函数中修改参数。
请暂时忘记指针。把这个和一粒盐一起吃。
引用是对象。当你通过引用传递时,你传递了对象。
当你通过值传递时,你传递了一个对象的副本;另一个对象。它可能具有相同的state,但它是不同的实例;一个克隆。
因此,如果您:
- 需要修改函数里面的对象
- 不需要(或不想)修改对象,但想避免复制它只是为了将它传递给函数。这将是一个
const
参考。
如果您:
- 想从同卵 双胞胎开始,不打扰原来的双胞胎
- 不关心复制对象的成本(例如,除非我想修改它,否则我不会通过
int
引用传递)。
在这里,看看这段代码:
#include<iostream>
struct Foo {
Foo() { }
void describe() const {
std::cout<<"Foo at address "<<this<<std::endl;
}
};
void byvalue(Foo foo) {
std::cout<<"called byvalue"<<std::endl;
foo.describe();
}
void byreference(Foo& foo) {
std::cout<<"called byreference"<<std::endl;
foo.describe();
}
int main() {
Foo foo;
std::cout<<"Original Foo"<<std::endl;
foo.describe();
byreference(foo);
byvalue(foo);
}
并像这样编译它:g++ example.cpp
运行:./a.out
并检查输出(您的计算机中的实际地址可能不同,但要点仍然存在):
Original Foo
Foo at address 0x7fff65f77a0f
called byreference
Foo at address 0x7fff65f77a0f
called byvalue
Foo at address 0x7fff65f779f0
请注意called byreference
地址与Original Foo
地址的相同之处(两者都是0x7fff65f77a0f
)。并注意called byvalue
地址有何不同(它是0x7fff65f779f0
)。
把它提升一个档次。修改代码如下所示:
#include<iostream>
#include<unistd.h> // for sleeping
struct Foo {
Foo() { }
Foo(const Foo&) {
sleep(10); // assume that building from a copy takes TEN seconds!
}
void describe() const {
std::cout<<"Foo at address "<<this<<std::endl;
}
};
void byvalue(Foo foo) {
std::cout<<"called byvalue"<<std::endl;
foo.describe();
}
void byreference(Foo& foo) {
std::cout<<"called byreference"<<std::endl;
foo.describe();
}
int main() {
Foo foo;
std::cout<<"Original Foo"<<std::endl;
foo.describe();
byreference(foo);
byvalue(foo);
}
以相同的方式编译它,并注意输出(输出中没有注释;为清楚起见包括在内):
Original Foo
Foo at address 0x7fff64d64a0e
called byreference
Foo at address 0x7fff64d64a0e # this point is reached "immediately"
called byvalue # this point is reached TEN SECONDS later
Foo at address 0x7fff64d64a0f
因此,该代码旨在夸大副本的成本:当您通过引用调用时,不会产生此成本。当您按值调用时,您必须等待十秒钟。
注意:我的代码是在 OS X 10.7.4 中使用 GCC 4.8.1 编译的。如果您在 Windows 中,您可能需要一些不同的东西unitsd.h
才能使sleep
呼叫正常工作。
也许这有帮助。
使用按引用传递的优点:您不必创建数据的副本,您只需在内存中传递指向它的指针。(想想如果你有一个巨大的物体,你会获得巨大的性能胜利)。您可以“返回”多个值 我知道 c/c++ 中的某些函数返回一个数字,其中一个参数是指向被操作数据的指针。
使用按引用传递的缺点:您必须小心修改传递的数据,因为它可能会导致您可能想要或可能不想要的副作用。
通过引用同样是通过指针手动传递变量,但通过引用不会让用户处理“容易搞砸”的指针。