What is a reference-to-pointer?
I recently saw a function that is being declared as:
void func(type* ¶m);
I already know the difference between type* param
and type& param
. How does the above differ from them? And when to use this? Is it advisable to do this?
Solution 1:
The above will be able to modify not only the pointed object, but also the pointer in itself. As an example, consider the below code:
void func(int*& ptr) {
*ptr = 1;
ptr = 0;
}
int main() {
int x = 0;
int* y = &x;
func(y);
}
At the end of the execution, x
has value 1
and y
is 0
(as you can see).
Notice that for the sake of the example I've used 0
as a null pointer, but if you are using C++11 you should probably use nullptr
instead (which does not have an overloaded operaror<<
for std::ostream
).
This concept can possibly be assimilated by taking a look at the following code:
template<class Type> using ptr = Type*;
ptr<int>& x;
or
std::unique_ptr<int>& x;
In these examples, x
is a reference to a type (ptr<int>
and then std::unique_ptr<int>
), which just so happens to be a pointer/class with pointer semantic (operator*
and operator->
).
A possibly interesting digression can be made on the position of a const
qualifier in the pointer. Consider these two instances:
void func(const int*& ptr)
void func(int*const& ptr)
Their meanings are:
- pointer by reference to a constant int.
- pointer by constant reference to an int.
And following the above analogy with ptr
they would be:
ptr<const int>&
ptr<int> const&
Therefore the first will fail to execute *ptr = 1
in the body of the function (because the int
is constant), but will happily execute ptr = 0
.
The second will behave conversely, allowing *ptr = 1
(because the pointed int is not constant), while disallowing ptr = 0
(because the pointer is constant).
Of course, in the case of:
void func(const int*const& ptr)
which in the ptr
analogy would be ptr<const int> const&
, both of them wouldn't be allowed.
And when to use this? Is it advisable to do this?
Like every feature, you'll find its usefulness when you'll need it. But just as a general idea, some people used it to reset the pointer after freeing a dynamically allocated resource (I'm not recommending this, see below).
Let's take this example:
free_my_int(int*& ptr) {
delete ptr;
ptr = nullptr;
}
int* x = new int(42);
free_my_int(x);
At the end of the execution, x
would be correctly freed and the pointer automatically set to nullptr
(null pointer). This was done to prevent ugly segmentation faults or "pointer freed has not been allocated" error messages caused by a missing ptr = nullptr
.
But with C++11 and C++14 there is very little use of pointers and even less of reference to pointers. Most of the things pointers where used for are not replaced with other standard construct (see std::optional
, std::unique_ptr
, std::shared_ptr
or std::reference_wrapper
for example).
Solution 2:
Let's compare all three options:
void func(type* param); // 'param' is a 'type*' variable
void func(type& param); // 'param' is a reference to a 'type' variable
void func(type*& param); // 'param' is a reference to a 'type*' variable
void func(type* param)
{
type x;
...
param = &x;
// Argument 'param' is regarded as a local variable in this function,
// so setting 'param = ...' will have no effect outside this function
}
Of course, if you do *param = ...
, then it will effect the contents of the memory pointed by param
. And you can also do param[5] = ...
for example, and effect other areas within that memory space.
void func(type& param)
{
type x;
...
param = x;
// Argument 'param' is regarded as a reference to a variable outside this
// function, so setting 'param = ...' will effect the referenced variable
}
Here, you will only change the referenced variable itself, so it's safer than declaring the function as void func(type* param)
, and then pass the address of type param
by calling func(¶m)
.
void func(type*& param)
{
type x;
...
param = &x;
// Argument 'param' is regarded as a reference to a variable outside this
// function, so setting 'param = ...' will effect the referenced variable
}
This is similar to declaring the function as void func(type** param)
, and then pass the address of type* param
by calling func(¶m)
, but again, it is safer for the same reason mentioned above.