The const reference vs pointer in C++ class constructor
tl;dr
There are many discussions between reference and pointers, it’s not always true to say “use references when you can, and pointers when you have to“. We should pay attention to the lifetime of the input parameters, choose the one that is safer and more efficient according to the use case.
How to pass a parameter?
Before we talk about const reference vs. pointer in C++ class constructor, let’s take a quick look at different ways of passing a parameter to a function.
1 | // 1. Passing by value. |
# | parameter | object copied? | original object mutable? |
---|---|---|---|
1 | passing by value | ✔️ | X |
2 | passing by const reference | X | X |
3 | passing by pointer | X | ✔️ |
4 | passing by const pointer | X | X |
As it can easily tell from the table above, passing by value introduces an extra object copy, while passing by pointer makes the original object mutable.
If you just want to pass some data, do some work with its value without updating it, you can choose #2 or #4, (don’t pick #1 as it’s could be expensive especially for large object, don’t use #3 as you could mistankenly update its value). Between #2 and #4, I’m leaning to #2 because reference is always non-nullable.
If you want to pass a parameter and update its value, pick #3 obviously.
I rarely use #1 in daily work.
Is passing const pointer always safe?
Not really.
1 | class Person { |
The above example uses const reference in a class constructor, it looks perfectly normal, right? What if we initialize Person
lie this:
1 | void SomeFunction() { |
When creating person
, the inside field name_
is just an alias of the temporary string Ox333
. Once person
is created, the temporary string goes out of scope, when it tries to print out the name (the content that the alias refers to), it doesn’t exist anymore. The behavior becomes unpredictable!
On OnlineGDB, it prints empty result. Live example: https://onlinegdb.com/HyOIEOS-U.
How to avoid this?
A straightforward fix is to store the value inside the class, so it’s always accessible, however, it requires an extra copy.
1 | class Person { |
Another fix, is to store the const pointer.
1 | class Person { |
By doing so, if the caller tries to create a person
with a temporary string, the compiler would complain it takes the address of a temporary variable. The unpredictable behavior is avoided proactivey by the compiler.
1 | std::string GetString() { |
Live example: https://onlinegdb.com/B15NNuHWL.
Can we do better?
We can use const reference to enforce the field name_
to be non-nullable. By doing so, we don’t need to worry if the field name_
is a nullptr or not.
1 | class Person { |
Check the live example: https://onlinegdb.com/Hy9hOurbU.
To sum up
There is no such a golden rule telling you when to choose const reference or pointer, what we can do is to understand how it works, choose the most suitable solution according to the specific case.