C++ 作为 const 和通过引用传递 - 值得吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5060137/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Passing as const and by reference - Worth it?
提问by Yukon
Possible Duplicate:
How to pass objects to functions in C++?
可能的重复:
如何将对象传递给 C++ 中的函数?
In my game I make excessive use of mathematical vectors and operator overloading of them.
在我的游戏中,我过度使用数学向量和运算符重载它们。
class Vector
{
float x, y;
};
That's basically all about my Vector class (methods excluded).
这基本上是关于我的 Vector 类(不包括方法)。
I am not an expert in C++ and I've seen and read about passing as const and passing by reference.
我不是 C++ 专家,我已经看到并阅读了有关作为常量传递和通过引用传递的信息。
So, where are the performance differences in the below code example?
那么,下面的代码示例中的性能差异在哪里?
Float RandomCalculation( Vector a, Vector b )
{
return a.x * b.x / b.x - a.x * RANDOM_CONSTANT;
}
// versus..
Float RandomCalculation( Vector& a, Vector& b )
{
return a.x * b.x / b.x - a.x * RANDOM_CONSTANT;
}
// versus..
Float RandomCalculation( const Vector& a, const Vector& b )
{
return a.x * b.x / b.x - a.x * RANDOM_CONSTANT;
}
- Which of the three should I use, and why?
What advantages for the optimization process of the compiler does each of the options have?
When and where do I have to be especially careful?
- 我应该使用这三个中的哪一个,为什么?
每个选项对编译器的优化过程有什么好处?
何时何地我必须特别小心?
回答by templatetypedef
Passing by const
reference is the preferred way to pass around objects as a smart alternative to pass-by-value. When you pass by const
reference, you take the argument in by reference (avoiding making any copies of it), but cannot make any changes to the original object (much as would happen when you would take the parameters in by value).
通过const
引用传递是传递对象的首选方式,作为传递值的智能替代方法。当您通过const
引用传递时,您将通过引用获取参数(避免制作它的任何副本),但不能对原始对象进行任何更改(就像按值获取参数时会发生的情况一样)。
If you consider these three functions:
如果考虑这三个函数:
void VersionOne(Vector v);
void VersionTwo(Vector& v);
void VersionThree(const Vector& v);
There are subtle distinctions between all of them. The first function, for example, will invoke the copy constructor when you pass in a Vector
so that it has its own local copy of the Vector
. If your copy constructor takes a while to run or does a lot of resource allocation and deallocation, this may be slow, but you can make any changes you want to the parameter without risking any changes propagating back up to the caller. There will also be a destructor call at the end of the function as the argument is cleaned up, and if this is too large a cost it may be advisable to avoid this setup. That said, for small objects it may be perfectly acceptable.
所有这些之间都有细微的区别。例如,第一个函数将在您传入 a 时调用复制构造函数,Vector
以便它拥有自己的Vector
. 如果您的复制构造函数需要一段时间运行或进行大量资源分配和释放,这可能会很慢,但您可以对参数进行任何您想要的更改,而不会冒任何更改传播回调用者的风险。随着参数被清除,函数末尾还会有一个析构函数调用,如果这个代价太大,最好避免这种设置。也就是说,对于小物体,它可能是完全可以接受的。
The second version of this function takes in a Vector
by reference, which means that the function can make any changes it wants to the Vector
and the changes will propagate back up to the caller. Whenever you see a function that takes an argument by non-const
reference, like this VersionTwo
function, you should assume that it will be modifying the argument, since if it weren't going to make any modifications, it would be taken by const
reference. You will most likely take in the value by reference if you need to make changes to the Vector
; for example, by rotating it, scaling it, etc. One tradeoff involved here is that the Vector
will not be copied when it is passed into this function, and so you will avoid a call to the copy constructor and destructor. This may have performance implications for your program, though if that's your reasoning you should probably go with pass by const
reference. One thing to note is that following a reference is very similar to following a pointer (in fact, most implementations of references just treat them as though they were automatically-dereferenced pointers), so there may be a small performance hit every time you access the data through the reference. Only profiling can tell you whether or not this is a major problem, though, and I wouldn't worry about it unless you had a specific reason to think it was at fault.
这个函数的第二个版本接受一个Vector
by reference,这意味着该函数可以对其进行任何它想要Vector
的更改,并且这些更改将传播回调用者。每当您看到一个函数通过非const
引用获取参数时,例如这个VersionTwo
函数,您应该假设它将修改该参数,因为如果它不打算进行任何修改,它将通过const
引用获取。如果您需要对Vector
;进行更改,您很可能会通过引用获取该值。例如,通过旋转它、缩放它等。这里涉及的一个权衡是Vector
当它被传递到这个函数时不会被复制,因此你将避免调用复制构造函数和析构函数。这可能会对您的程序产生性能影响,但如果这是您的推理,您可能应该通过const
引用传递。需要注意的一点是,跟随引用与跟随指针非常相似(实际上,大多数引用的实现只是将它们视为自动取消引用的指针),因此每次访问数据通过参考。但是,只有分析才能告诉您这是否是一个主要问题,除非您有特定的理由认为它有问题,否则我不会担心。
The final version of this function takes in a Vector
by const
reference, which, like passing by regular reference, avoids any copying. However, when taking the Vector
by const
reference, you are prohibited from making any changes to the Vector
inside the function, so clients can assume that the Vector
will not be modified. (Yes, technically it could be modified if it is poorly-written or has mutable
data members, but we'll ignore that for now. It's the high-level idea that's important here). This option would be good if you wanted to be able to inspect the value in the function without copying it and without mutating it.
这个函数的最终版本接受一个Vector
按const
引用,就像按常规引用传递一样,避免了任何复制。然而,服用时Vector
的const
参考,您是从做任何更改禁止Vector
在函数内部,因此客户可以假设Vector
将不会被修改。(是的,从技术上讲,如果它写得不好或有mutable
数据成员,则可以对其进行修改,但我们现在将忽略它。这里的高级思想很重要)。如果您希望能够检查函数中的值而不复制它并且不改变它,那么这个选项会很好。
There is one more difference between pass-by-reference and pass-by-const
-reference, and that's the behavior of the function on rvalues. If you have a temporary Vector
object - either you created it explicitly by writing Vector()
or by doing some mathematical operation on it like writing v1 + v2
- then you cannot pass that temporary Vector
into a function that takes its parameter by reference because references can only bind to lvalues. The idea is that if you have a function like this:
传递引用和传递引用之间还有一个区别const
,那就是函数对右值的行为。如果您有一个临时Vector
对象 - 无论是通过写入显式创建它,Vector()
还是通过对其进行一些数学运算(如写入)v1 + v2
- 那么您不能将该临时对象传递Vector
给通过引用获取其参数的函数,因为引用只能绑定到左值。这个想法是,如果你有这样的功能:
void DoSomething(Vector& v) {
v.x = 0.0f;
}
Then it doesn't make sense to write
那么写就没意义了
DoSomething(v1 + v2);
Since this would be changing the x
field of a temporary expression. To prevent this, the compiler will refuse to compile this code.
因为这将改变x
临时表达式的字段。为了防止这种情况,编译器将拒绝编译此代码。
However, C++ makes an exception and lets you pass rvalues into functions that take their argument by const
reference, because, intuitively, you shouldn't be able to modify an object through a const
reference. Thus this code is perfectly legal:
但是,C++ 有一个例外,它允许您将右值传递给通过const
引用获取其参数的函数,因为从直觉上讲,您不应该能够通过const
引用修改对象。因此这段代码是完全合法的:
void DoSomething(const Vector& v) {
cout << v.x << endl;
}
DoSomething(v1 + v2);
So, to summarize-
所以,总结一下——
- Pass-by-value and pass-by-
const
-reference imply similar things - you want to be able to look at the value without being able to modify it. - Any time you could use pass-by-value you could instead use pass-by-
const
-reference without affecting the correctness of the program. However, there are performance tradeoffs between the indirection of the reference and the cost of copying and destructing the parameter. - Pass-by-non-
const
-reference should be used to indicate "I want to modify the argument." - You cannot pass rvalues into functions that take their arguments by non-
const
reference.
- pass-by-value 和 pass-by--
const
reference 意味着类似的事情 - 您希望能够查看值而不能修改它。 - 任何时候你可以使用值传递,你可以改为使用传递--
const
引用而不影响程序的正确性。但是,在引用的间接性与复制和破坏参数的成本之间存在性能权衡。 - Pass-by-non-
const
reference 应该用于表示“我想修改参数”。 - 您不能将右值传递给通过非
const
引用获取其参数的函数。
Hope this helps!
希望这可以帮助!
回答by Maarten
Do you want to copy the objects passed to the function?
If you pass the objects "Vector a"
and "Vector b"
by value, you have to deal with the overhead of copying them. For small structures the overhead is negligible.
你想复制传递给函数的对象吗?如果您通过对象"Vector a"
和"Vector b"
按值传递,则必须处理复制它们的开销。对于小型结构,开销可以忽略不计。
If you pass the objects by reference or by pointer, you don't have this overhead. However, passing by pointer or reference potentially allows for modification of the object:
如果您通过引用或指针传递对象,则没有这种开销。但是,通过指针或引用传递可能允许修改对象:
The const
keyword in front of the object name is used to guarantee that your function does not modify the objects that are passed to the function by reference or pointer. Not only will this tell other programmers that your function is safe to use, it is also strictly enforced by the compiler. The const
keyword does not have an impact on performance when using it for this purpose.
const
对象名称前面的关键字用于保证您的函数不会修改通过引用或指针传递给函数的对象。这不仅会告诉其他程序员您的函数可以安全使用,而且编译器也会严格执行。将const
关键字用于此目的时,它不会对性能产生影响。
If you have large objects, pass them by const reference
or const pointer
.
If you want to modify the object that is passed to the function, use a reference or a pointer.
If your object is a small structure you can pass it by value.
如果您有大对象,请通过const reference
或传递它们const pointer
。如果要修改传递给函数的对象,请使用引用或指针。如果您的对象是一个小结构,您可以按值传递它。
回答by Edward Strange
You'll get a lot of answers to this question claiming one thing or another. The truth of the matter is that you need to test it. Your object is fairly small so that passing by value may be as fast or faster than passing by reference. Only by profiling your code will you be able to know.
你会得到很多关于这个问题的答案,声称一件事或另一件事。事情的真相是你需要测试它。您的对象相当小,因此按值传递可能与按引用传递一样快或快。只有通过分析您的代码才能知道。
回答by fredoverflow
You only pass by reference to non-const if you want to change the arguments and have the client observe those changes.
如果您想更改参数并让客户端观察这些更改,则只能通过引用传递给非常量。
If you don't want to change the arguments, pass by reference to const or by value.
如果您不想更改参数,请通过引用传递给 const 或通过值。
If you want to change the arguments but have no effect on the client, pass by value.
如果您想更改参数但对客户端没有影响,请按值传递。
回答by 6502
May be now things are changed but I did some tests many years ago with several C++ compilers and different variations (e.g. member or non-member operators, operator+
defined in terms of operator+=
or not, pass by value or by ref and the like).
可能现在事情发生了变化,但我多年前用几个 C++ 编译器和不同的变体做了一些测试(例如,成员或非成员运算符,operator+
根据operator+=
与否定义,通过值或通过 ref 等)。
I checked both timings and the generated assembler code. The results where highly dependent on which compiler was used... the best for one was not the best for another.
我检查了时间和生成的汇编代码。结果高度依赖于使用哪种编译器......对一个最好的并不对另一个最好。
Also, sadly enough, at that time I was never able to get the same performance of manually unrolled C code performing the same computations. Close yes... but C was still somewhat faster.
此外,令人遗憾的是,当时我永远无法获得与执行相同计算的手动展开的 C 代码相同的性能。关闭是的......但C仍然有点快。
回答by Cameron Skinner
The short answer is that there will be little difference in practice.
简短的回答是实践中几乎没有区别。
Now for the long answer:
现在为长答案:
There will be a performance difference between the first and second versions, and there maybe a difference between the second and third.
第一个和第二个版本之间会有性能差异,第二个和第三个版本之间可能会有差异。
When you call the first version (Vector a, Vector b
) a copy of each of the arguments will be made on the stack. This involves allocating stack memory and copying the member fields of the Vector class.
当您调用第一个版本 ( Vector a, Vector b
) 时,每个参数的副本都将在堆栈中生成。这涉及分配堆栈内存和复制 Vector 类的成员字段。
The second version will not copy the Vector objects. Instead, memory will be allocated for the two references (probably 4 or 8 bytes each depending on your machine) and populated with the address of the caller's Vector objects. This is a little less memory allocation and a couple fewer writes.
第二个版本不会复制 Vector 对象。相反,将为两个引用分配内存(每个可能有 4 或 8 个字节,具体取决于您的机器)并填充调用者的 Vector 对象的地址。这是更少的内存分配和更少的写入。
The third version will probably not be any more performant. The const
keyword is useful for ensuring that your code executes without unexpected side effects so it's probably good practice to use it, but is unlikely to result in faster code. The compiler canuse const as a hint that allows some optimisations but it might perform those optimisations anyway.
第三个版本可能不会有更高的性能。该const
关键字对于确保您的代码在没有意外副作用的情况下执行很有用,因此使用它可能是一种很好的做法,但不太可能产生更快的代码。编译器可以使用 const 作为允许进行一些优化的提示,但无论如何它可能会执行这些优化。
In your case, the Vector class is so small that there is unlikely to be any practical difference unless you are making a huge number of calls. It's much more important to understand the different semantics between your first and second versions. In the first version, changes to a
and b
do not impact the caller's view of those objects. In the second version (with references) changes to a
and b
doaffect the caller's view.
在您的情况下, Vector 类非常小,除非您进行大量调用,否则不太可能有任何实际差异。了解第一个和第二个版本之间的不同语义要重要得多。在第一个版本,更改为a
和b
不影响这些对象的调用者的观点。在第二个版本(带参考)中,更改a
并b
确实影响了调用者的视图。
Long story short: Get the semantics right first, then worry about optimisation. Be wary of premature optimisation. If you reallywant to optimise this kind of thing then get a good book on the internals of C++ and get a deep understanding of exactly what the compiler does when it encounters pass-by-value and pass-by-reference functions.
长话短说:首先获得正确的语义,然后再考虑优化。警惕过早的优化。如果你真的想优化这种事情,那就找一本关于 C++ 内部原理的好书,深入了解编译器在遇到传值和传引用函数时到底做了什么。
In your case I'd suggest using version 3 because the 'const' shows your intent and passing by reference removes an unnecessary copy.
在您的情况下,我建议使用版本 3,因为“const”显示您的意图,并且通过引用传递删除了不必要的副本。
EDIT: templatetypedef said it better.
编辑:templatetypedef 说得更好。