C++ is backward compatible with C so it inherited pointers. A quick recap if you don't know what a pointer is: a pointer is a variable that holds the memory address of an object. Declaring a pointer involves the type of object we are pointing to and the symbol '*'.
Example:
int value = 10;
int *ptr_value = &value;
double vaule2 = 506.305;
double *ptr_value2 = &value;
We have our pointer, but how do we get an object's memory address? With the ampersand symbol (&).
To access data through a pointer, we need to do what is called dereferencing. This again includes the use of the * symbol.
printf("value: %d\n", *ptr_value);
printf("value2: %f\n, *ptr_value2);
Now that the recap is over, we can move onto references.
C++ introduced references as an alternative to pointers. References were designed to be simpler, safer, and easier to read when in use. To create a reference is similar to how we declare a pointer. First, we need the type and the symbol '&'.
int value = 200;
int &ref_value = value;
Notice we don't have anything extra on the right-hand side as we had with pointers. The only extra symbol in this statement is the & symbol on the left.
Another benefit is obvious when it is time to retrieve value from the reference:
std::cout << ref_value << "\n";
There are no extra symbols cluttering the code. Viewing small examples of dereferenced pointers may not show the cognitive burden that may be placed on someone when viewing a larger block of code that has several dereferences going on in a short amount of time.
References, as aforementioned, are safer. Going back to pointers for a moment, a pointer can be within a state of "invalidness". This could mean two things:
- The pointer is declared but has no initializer and is subsequently default-initialized
Within local scope, if a variable is not provided with an initial value at creation, a garbage value is left there. Most likely, it was some value left over from some memory previously that we are now occupying. Accessing a default-initialized object is undefined behavior and dereferencing a default-initialized pointer is also undefined behavior.
- Null Pointers
A null pointer is a special kind of pointer. Usually, it is a way to say, "Hey, I'm not in use right now, but I am still explicitly initialized!". Null pointers have a place in API design. Many C functions that return a pointer may instead return a null pointer on error. Despite it being "safer" than the previous type of invalid pointer, dereferencing a null pointer is undefined behavior.
At the creation of a reference, it must have an initial value to bind. Failure to do so is a compile time error. Also, a reference is bound to one value for its entire lifetime.
However, references have their weaknesses, too. Like pointers, a reference can "dangle": referring to a space of memory that is freed.
Example:
int &return_five()
{
int value = 5;
return &value;
}
int some_value = return_five();
std::cout << some_value << '\n'; // undefined behavior
return_five()
is a function that creates a variable and returns it. This may appear innocent to the inexperienced but this program is in error. This value being returned is created on the stack and when the function returns, the stack frame is destroyed and so is the variable inside.
By the time we return the main function, the reference some_value
is now referring to freed memory and is a dangling reference.
The Conclusion
References, in theory, are a better alternative to pointers in some regards. Providing a simpler syntax at creation and accessing bounded values. The restraints in place eliminate some of the problems that using pointers may arise.
Top comments (6)
I don't write a lot of C++ but shouldn't your code example declare
some_value
as a reference toint
to match the return type of thereturn_five
function?No, because the non-reference variable will contain the value of the bounded value of the reference.
Output:
Oh yeah okay you're dereferencing when assigning to
some_value
, gotcha. Just another question: if you useauto
, what would the inferred type be?Does
new_value
remain a reference or now contains the dereferenced value?Not entirely sure as
auto
in some instances is tricky. I believe in your example thatnew_value
will the typeint
.It will be int&, I think: en.cppreference.com/w/cpp/language...
A reference is, of course, a bit of syntactic sugar for a pointer. But there are two differences:
The last line prints two, because the reference is bound to
one
, and you've just changed the value. The pointer, now also bound to one, therefore prints the same value.You can make a pointer non-rebindable with a
const
after the point, though. References, and const-pointers, are similar to the effect of theconst
keyword in Javascript, and thefinal
keyword in Java.Calling any of these functions with s1 is fine - there's always something to reference. f2() might change the string though.
But you cannot call f2 with either an expression, or a literal - because f2 says it might change the string, f2 cannot be called with a temporary.
No such protections exist for pointers.
You can, though, create a nullptr reference quite easily:
Of course, the moment you do anything with ref, you're implicitly referencing it, and you'll experience the joy of undefined behaviour.