C++ "Best"参数传递方法
C++ "Best" Parameter Passing Method
我今天正在编写一个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,在回答中处理了性能和价值传递。如果你是来找答案的,那就全部检查一下!
如果期望有效,我建议通过引用传递您的参数。这将是一个设计优化,并将您从防御性编程中拯救出来。
引用不能为空,而指针可以为空。如果你正在处理指针,你需要验证是否给定的指针是有效的(非空),不管它是在原始形式或包装在托管容器(shared_ptr),在使用它们之前。
所以我要做的是选择#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)。如果按值传递确实太慢,则通过const引用或指向const的指针传递。
但是不要低估按值传递的潜在速度优势,这源于寄存器相对于内存和指令重排序的优势。请注意,这些优势在每一代CPU中变得更加明显。
通过指针传递和通过引用传递实际上是相同的,只是语法不同。我更喜欢通过指针传递,因为它使事情显式:
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)
如果一切都是自己设计的,那么任何一天都可以引用。按照惯例,现代c++几乎不应该在任何地方有原始指针。动态分配的对象应该在资源管理容器(shared_ptr
或unique_ptr
,或weak_ptr
)中传递,但对于大多数操作来说,通过(const)引用传递需要修改的参数或重量级类型的参数是主要方式。不要忘记,如果你有可移动的类型,按值传递可能是一个可行的选择。
使用说明:
- const引用,如果对象未被修改
- 指针,如果对象被修改,或者可以为空
- 值,如果对象很小并且您关心性能,或者如果您需要在函数内复制对象。这允许编译器选择复制/移动参数的最佳方式。
- std::unique_ptr如果所有权转移到函数。
您可以查看https://www.boost.org/doc/libs/1_51_0/libs/utility/call_traits.htm库,它会自动将类型转换为最佳参数类型
- 将成员函数指针作为参数传递给模板方法
- 在 C++ 中将非指定类型作为参数传递的最佳方法?
- 如何在方法中传递结构参数
- 如何将成员函数作为参数传递并在派生对象上执行方法列表
- 将带有模板化方法的类作为带有GCC的模板参数传递
- 将派生类作为参数传递给方法,该方法是具有智能指针的基类
- 将私有数据成员作为默认参数传递给该类的公共方法时出错
- 如何将提升回调作为参数传递给方法?
- 将方法作为模板化参数传递 C++11
- 最佳做法是在方法中传递参数
- 将 std::ofstream 对象作为参数传递给类方法
- 在构造函数上传递参数或在每个方法上传递参数
- 将给定的 vararg 参数传递给另一个函数的适当方法是什么?
- 将指针类方法作为参数传递给其他类方法C
- 基本C - 构造contaning对象引用,并将其作为方法参数传递
- JNI——可以将复杂类型作为本机方法参数传递
- OOP 设计 - 将类成员变量作为类中的方法参数传递
- C++将动态方法指针作为不同类的方法参数传递
- 如何将指向一个方法的指针作为另一个方法参数传递
- 将内联双数组作为方法参数传递