C++ get method - returning by value or by reference
I've go a very simple question, but unfortunately I can't figure the answer myself.
Suppose I've got some data structure that holds settings and acts like a settings map.
I have a GetValue(const std::string& name)
method, that returns the corresponding value.
Now I'm trying to figure out - what kind of return-value approach would be better. The obvious one means making my method act like
std::string GetValue(co开发者_运维百科nst std::string& name) const
and return a copy of the object and rely on RVO in performance meanings.
The other one would mean making two methods
std::string& GetValue(...)
const std::string& GetValue(...) const
which generally means duplicating code or using some evil constant casts to use one of these routines twice.
#
Q
What would be your choice in this kind of situation and why?
Actually I would probably use:
std::string GetValue(const std::string& name) const;
// or
const std::string* GetValue(const std::string& name) const;
void SetValue(std::string name, std::string value);
Setter first:
- Passing by value in
SetValue
allows the compiler some optimizations that cannot be made with pass by const-reference, it's been explained in a article by Dave Abrahams, "Want Speed? Pass by Value." - Using a
Setter
is usually better, because you can check the value being set whereas with a plain reference you have no guarantee that the caller won't do anything stupid with the data.
For the getter:
- Returning by copy seems a waste since most times you won't modify the object returned (for a settings map), however pointer or reference do mean that the object aliased will live long enough, if you can't guarantee it then the point is moot: return by value. Also copy allow to avoid exposing an internal detail, for example if you were suddenly to switch to
std::wstring
because you need some settings in UTF-8... - If you need performance, then you are ready to do some concessions in this department. However a reference does not allow you to signal the absence of the property (unless you have some magic value) while the pointer makes it easy (NULL is already cut out for you).
That depends on the usage. Should GetValue("foo") = "bar"
make sense? In that case, return by value does not do what you want.
If you can return by const reference, do. The only reason to return a memory-managing class like std::string by value is because it's a temporary string.
This is a very "sensitive" point of C++. IMHO it's one of the weakest points of C++ design. AFAIK there's no really good choice, which will be both look and perform good.
One point that should be mentioned that RVO optimization usually does only a part of the job. Without it a typical expression like this:
std::string txt = GetValue("...");
will actually produce 2 copies! Depends on the compiler, but usually RVO optimization eliminates just one copy, however the other is still there.
I'm adding this answer to avoid future readers who are less experienced with C++ from writing code that produces undefined behaviour.
The question doesn't specify whether the class that the Getter function belong to stores a copy of the value to be returned.
Let us deal with the return by value case first. This is the option you would normally choose.
std::string GetValue(const std::string& name) const
This is valid C++ and does not produce undefined behaviour.
The OP didn't specify how the return value is produced. It might be something like this:
std::string GetValue(const std::string& name) const
{
std::string temporary = function_of_name(name);
// for example:
// std::string temporary = "Hello " + name + "!";
return temporary;
}
In the above example, a temporary object is created inside the scope of GetValue
. At the end of function scope it is returned, by value, which means that either a copy of the object is made, or more likely the compiler performs return value optimization (RVO) (pre and post C++ 11) and the copy is "optimized out" or "elided".
Please do not confuse this with modern C++ 11 and later "move semantics". This is a different but related concept. RVO existed before move semantics, and most good C++ 03 compilers will perform RVO.
The point being that returning any kind of reference here for example std::string&
produces undefined behaviour, because the object goes out of scope at the end of the function body and the returned reference is now pointing to something that has been deleted/removed from memory. (A reference is just a pointer with extra syntactic sugar applied.)
Second Case
If the class stores a copy of the object to be returned then returning a reference is a valid thing to do. However it is not likely to be much faster than the first RVO case.
I can expand on this to add more detail later if necessary, but hopefully it is now clear why this is the case.
精彩评论