我今天正在编写一个C ++类,我编写了一个函数,它将参数作为参考而不是指针,这是我很少做的事情。我总是通过指针传递。所以我准备改回来,然后我意识到 - 我不知道我是否应该,或者它是否重要。
所以我转向你们。我有三种传递参数的方法:
//1: By pointer
Object* foo(Object* bar) {...}
//2: By reference
Object& foo(Object& bar) {...}
//3: By value (just for completeness)
Object foo(Object bar) {...}
假设#3因性能原因而退出(是的,我知道编译器已经相当不错了,但仍然如此),其他两个或多或少相当。
那么:什么是“最好的”方法?指针?参考文献?两者的某种组合?或者甚至重要吗?技术原因是最好的,但风格原因同样好。
更新:我已经接受了YeenFei的回答,因为它处理了为我赢得的差异(即使我然后明确地忽略了他的建议 - 我喜欢将NULL作为选项... )。但每个人都提出了很好的观点 - 特别是GMan(在评论中)和Nemo,在回答性能和价值传递的过程中。如果您在这里寻求答案,请检查所有答案!
答案 0 :(得分:4)
如果预期有效,我建议通过引用传递你的论点。这将是一种按设计优化,可以使您免于defensive programming。
当指针可以时,引用不能为空。 如果您正在处理指针,则在使用指针之前,您需要验证给定指针是否有效(非空),无论它是原始形式还是包装在托管容器(shared_ptr)中。
答案 1 :(得分:3)
所以我打算选择#3。请考虑以下代码:
struct Foo {
int x;
int y;
};
Foo
add(Foo a, Foo b)
{
Foo result;
result.x = a.x + b.x;
result.y = a.y + b.y;
return result;
}
Foo
add2(Foo &a, Foo &b)
{
Foo result;
result.x = a.x + b.x;
result.y = a.y + b.y;
return result;
}
尝试检查生成的程序集。注意add
几乎完全是注册操作,安排得很好。请注意add2
如何进行大量内存访问而不进行任何重新排序。
我写了一个main
,称这些功能各100亿次。结果? add
耗时22秒,add2
耗时26秒。即使对于这个简单的例子,按值传递版本的性能也是10-20%更好。
当然,这个决定应该主要基于函数的语义:你需要NULL才能成为合法值吗?如果是这样,显然你需要一个指针。你需要修改对象吗?然后使用指针或引用。
但是如果你不需要修改对象,除非对象很大和/或有一个非平凡的复制构造函数(例如std :: string),否则更喜欢按值传递它们。如果by-value确实太慢,请通过引用const或指向const的方式传递。
但是不要低估传递值的潜在速度优势,这源于寄存器与存储器和指令重新排序的优点。请注意,每一代CPU都会使这些优势变得更加明显。
答案 2 :(得分:2)
除了语法之外,通过指针和引用传递实际上是相同的。我更喜欢通过指针传递,因为它使事情变得明确:
Object bar;
ptr_foo(&bar); // bar may change
ref_foo(bar); // can bar change? Now I need to go look at the prototype...
val_foo(bar); // bar cannot change. (Unless you use references here and there)
传递值和指针之间唯一的技术偏好,正如您所触及的那样,该类是否足够大以使其传递缓慢。
答案 3 :(得分:0)
如果您自己设计所有内容,请随时参考。习惯性的现代C ++应该几乎没有任何原始指针在任何地方突出。动态分配的对象应该在资源管理容器(shared_ptr
或unique_ptr
或weak_ptr
(如果适用))中传播,但对于大多数传递(const)引用的操作是传递参数的主要方法需要修改或者是重量级的。不要忘记,如果你有可移动的类型,那么通过值传递可能是一个可行的选择。
答案 4 :(得分:0)
使用:
答案 5 :(得分:0)
您可以看看https://www.boost.org/doc/libs/1_51_0/libs/utility/call_traits.htm库,它会自动将类型转换为最佳参数类型。