There is a set of good rules to determine whether pass by value or const reference
For constructor as following, how to determine it?
class A
{
public:
A(string str) : mStr(str) {} // here which is better,
// pass by value or const reference?
void setString(string str) { mStr = str; } // how about here?
private:
string mStr;
};
From what I understand: when you pass by value, the function makes a local copy of the passed argument and uses that; when the function ends, it goes out of scope. When you pass by const reference, the function uses a reference to the passed argument that can't be modified.
Not just a copy; it is also a const copy. So you cannot modify it, invoke any non-const members from it, or pass it as a non-const parameter to any function. If you want a modifiable copy, lose the const decl on protos .
By definition, pass by value means you are making a copy in memory of the actual parameter's value that is passed in, a copy of the contents of the actual parameter.
Use pass-by-reference if you want to modify the argument value in the calling function. Otherwise, use pass-by-value to pass arguments. The difference between pass-by-reference and pass-by-pointer is that pointers can be NULL or reassigned whereas references cannot.
In this particular case, and assuming C++11 and move construction/assignment for strings, you should take the argument by value and move it to the member for the constructor.
A::A(string str) : mStr(std::move(str)) {}
The case of the setter is a bit trickier and I am not sure whether you really want/need to optimize every bit of it... If you want to optimize the most you could provide two overloads, one taking an rvalue reference and another taking a const lvalue reference. At any rate, the const lvalue reference is probably a good enough approach:
void A::setString(string const& str) { mStr = str; }
Why the difference?
In the case of the constructor, the member is not yet built, so it is going to need to allocate memory. You can move that memory allocation (and actual copying of the data, but that is the leaser cost) to the interface, so that if the caller has a temporary it can be forwarded without an additional memory allocation.
In the case of assignment the things are a bit more complicated. If the current size of the string is large enough to hold the new value, then no allocation is required, but if the string is not large enough, then it will need to reallocate. If the allocation is moved to the interface (by-value argument), it will be executed always even when it is unnecessary. If the allocation is done inside the function (const reference argument) then for a small set of cases (those where the argument is a temporary that is larger then the current buffer) an allocation that could otherwise have been avoided would be done.
The article you site is not a good reference for software engineering. (It is also likely out of date, given that it talks about move semantics and is dated from 2003.)
The general rule is simple: pass class types by const reference, and other types by value. There are explicit exceptions: in keeping with the conventions of the standard library, it is also usual to pass iterators and functional objects by value.
Anything else is optimization, and shouldn't be undertaken until the profiler says you have to.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With