When to use pointers and references?

Hello! I HAVE googled this a lot. So at an intellectual level I realize:

Passing/variables by value are useful because changes to the variable will not reflect the one from which it is called.

Passing/variables by pointer is faster because it does not make a copy. It also allows the pointer variable to be changed at the root of the function.

Passing/variables by reference is similar to pointers but they cannot be reassigned and are aliases instead of memory addresses.

- At least that's my understanding right now.

My problem is that I cannot for the life of me differentiate WHEN to use which one. Maybe I'm just not understanding when I need a copy of a variable and when I can get away with using a pointer/reference. (I should try to use them whenever I can because they're faster, right?) - googling, someone also said to only use them whenever it is your last option. (which did not help me understand when to use them)

If pointers/reference variables are faster why not always use them?

Right now I can't consciously think "ok, this variable should be a copy this could be a pointer/reference" - Is there any type of rule I can go by when trying to apply these 3 variable types? References are basically memory addresses, correct? When do you need to use that aspect?

Also, pointers/references can be used on classes and functions? Why?

Maybe someone could give me an example of when you would definitely need a pointer and when you would definitely need a reference? Also, Why would you use one over the other?

Sorry, I am really struggling with this!

Thanks!
First when passing variables by value a copy of the value is made when passing to a function. This copy can be expensive (in terms of resources or time) when dealing with non-POD (Plain Old Data) or non trivial types like std::string, std::vector, etc so you usually want to avoid the copy by passing by reference or by pointer instead.

With POD types is is usually just as fast to copy the value as it is to pass by pointer or reference. So the only time you should consider passing by pointer of reference for a POD type is when you want the function to be able to modify the value.

Now as to when to pass by pointer or to pass by reference, prefer pass by reference. IMO, Only use pass by pointer when absolutely necessary, such as when it is possible the value to be a nullptr.


First, this "speed" notion depends entirely on what we are comparing.

An integer, for example, is usually about the same size as a pointer, depending on how the integer is declared.

There is no difference in speed when pushing a copy of an integer when compared to pushing a pointer to an integer, or a reference to an integer.

The speed difference comes into play when passing larger structures (classes, structs) by value, as compared to pointer or reference.

If you're passing structures to a function, you probably want the speed benefit, so unless there is a reason to want a copy (a duplicate/constructor/destructor implied), you don't want to pass those by value.

If you're passing anything, int, float, structure....and you want the calling code to receive the changes made to those values inside the function, you can't pass those by value.

To be clear:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void f1( int a )    { a *= 2; }
void f2( int & a ) { a *= 4; }
void f3( int * a )  { (*a) *= 8; }

int main()
{
 int x = 5;

 f1( x ); // x is still 5, f1 used a copy
 f2( x ); // x is now 20, it did not use a copy
 f3( x ); // x is now 160, it did not use a copy

 return 0;
}



If you don't need or care that the caller will not see the changes made inside the function, you can pass by value (the function uses a copy).

If you need the calling code to be aware of the changes made to the value inside the function, as in f2 or f3 above, you can't pass by value (you could use a return from the function, but that's another subject).

Now....about pointer vs reference.

The pointer is from C. It is from the origin of the language back in the 70's. It is an older concept.

In Modern C++ we avoid pointers wherever possible. This implies you should prefer references unless you have no choice be to "devolve" toward a pointer.

Raw pointers come at us from the operating system, as most operating systems are written in C.

Sometimes you're working at a raw level for the sake of extreme performance, and you may create some custom memory management system, or you're processing video data, audio data, image information...the point here is that by the time you're doing that stuff, you know you need pointers and don't need to even ask anymore.

Until you're going that, you're best to avoid pointers entirely until you're ready to really study them (and their usage).

In the 90's, we might:

A * p = new A();

We'd do this for dynamic allocation of A. Maybe A is polymorphic (has a virtual base class). For whatever reason I could not just use an "A" on the stack, if I just had to make one from the heap (that could survive beyond the local function, for example), then I must create it with new.

Until modern C++.

Now, one should far prefer:

std::unique_ptr< A > p = std::make_unique<A>();

Or the related "make_shared" if using shared_ptr.

Here, there are no naked pointers. "p" will still work like a pointer, fast as a pointer, and "A" is dynamically created. However, I will not have to remember to delete it. That will work automatically.

I also can't accidentally delete it twice because I screw up the management of a raw pointer that owns it.

...and a bunch of bugs associated with raw pointers.

Now, one of the things you didn't ask about is implied with:

void f( std::unique_ptr<A> & p );

That might not strike you, so look at the rawer version of it

void f( A *& p );

That's a reference to a pointer.

Do not blow a fuse contemplating it. It's rather simple, and has a purpose, just something more to contemplate (and there's more, but I'll pause here).



Probably THE biggest difference between a reference and a pointer:

A pointer can be null, not pointing to valid memory.

A reference can never be null, it will always point to valid memory.

The syntax of how you retrieve the stored value differs between the two as well.
Without getting into the specifics of modern practices like make_shared, smart pointers, etc, I was just about to post what Furry Guy just posted: A pointer can be null, a reference can't be null. It sounds like you already understand this in theory, but you don't see why it's useful in practice.

A construct in a language is something that is created to solve a problem. If you haven't yet had a place in your code where you felt you've needed to use pointers/references to solve an issue, then simply don't use them. So the question is, what does using a pointer help solve, or what advantage does it let you achieve? Same question for references.

• (1) As was already mentioned, passing by reference allows you to still reference an object from a different function, without having to make a copy of the object. So the problem this is solving is one of efficiency. If you have very large objects, like vectors of many elements, it becomes necessary.
• (2) One place where a reference shines is where you have multiple objects that need to use the same object (a shared third object), and you know that the referenced object will outlive the other object. If you could only work with values, each object would need to have its own copy of the third object, and you lose any meaningful connection between these two objects. A reference lets both objects, well, reference the same third object.
• (3) Where a pointer shines is similar to where a reference shines, but a pointer allows more freedom (and also, mistakes). It allows you to reference an object, but also change which object your referencing to on the fly.
• (4) Run-time polymorphism with dynamic arrays of objects. If you need to iterate over polymorphic objects that are stored as elements in an array, you simply can't store the base class of those objects in the array (search: object splicing). And you can't, by language design, have an array of references. So you have to store an array of pointers to those objects, especially if you want to add and remove elements to this array at runtime.

(2) For example, think of a family tree. You and your brother both have a mother. But your mother is her own being. You and your brother shouldn't have copies of your mother; they have references instead.
(3) In this same family tree example, imagine that when you were born, the hospital accidentally swapped you at birth with another family's baby. When you were born, you had a reference to your original mother, but now you have a different "mother"! In this case, you can simply re-assign the pointer so that you are now pointing to your fake mother and not your real mother. A reference couldn't do this, because once a reference is created, it can't be re-assigned to another object.

Also, I've probably already failed in keeping this as concise as possible, but one thing to note is that everywhere a reference is used, a pointer could also be used; C only had pointers, not references. But pointers come at the cost of ugly syntax and error-prone dereferencing.

Prefer values when feasible. Prefer references to pointers. Only use pointers if you have to.
Last edited on
another thing on the speed question ... pointers are 2 variables. You do about twice as much work at the assembly language level to use them.

for example, take a pure integer...
if you pass an integer to a function by reference, the reference IS the integer variable, so the assembly is something simple like move value in memory to cpu register (one statement). If you passed a pointer to it, you have steps like move pointer's value to register first, then go to that location in memory and move the value there to cpu register. Optimize may be able to short out some of this but often it is unavoidable.

In terms of pure speed, references are either the best, or at worse equal to, any other option, in MOST code. Again, optimization has gotten really smart and funky so I hesitate to say always instead of most there. That does not mean always use references (plenty on that above), its just a review of the mechanics.
Last edited on
@jonnin,

You bring up a good point, worrying about speed of a particular bit of code can be at best nothing to worry about and at worst counter-productive.

Modern compilers do a very good job of optimizing code for speed or size of executable. Best to get the code to work with as few run-time problems as possible, and let the compiler worry about speed for the most part.

The compiler might create machine code for references that would be a C++ representation of pointers or vice versa.
Last edited on
Thanks so much for the responses. I will have to refer to this thread as I explore the subject more and get more experience. From what I am hearing it seems to be only use references when needed and avoid pointers. I think this will just take some time and experience to work out for me.

I think this seems to be the general rule here:
If you're passing anything, int, float, structure....and you want the calling code to receive the changes made to those values inside the function, you can't pass those by value.


And also, I learned that pointers are not necessarily faster.

Thanks again! I appreciate it!
Last edited on
If it isn't too late, I'll add that modern C++ references really the safe, easy way to get the cat's meow. Pointers are old school objects that references are designed to obsolete, as far as it is possible.

Use pointers when legacy libraries require it; prefer references otherwise.

And, when using pointers, try to wrap them in unique_ptr or shared_ptr as possible.

Hope this helps.
This is actually pretty simple.

If it's possible to pass no object to the function:

Pass by pointer

If you want the caller to be aware of changes to the object:

Pass by reference.

If you don't want the caller to be aware of changes to the object:

- Pass by value if the object is small (char, short, int or long)
- Pass by const reference if the object is larger
If it's possible to pass no object to the function: Pass by pointer


This being the future, my local style guide has now switched to using std::optional for this. Same effect but the intent is more clearly communicated.
@Repeater,

Checking to see if a pointer doesn't point to a valid object is easy:
some_pointer == nullptr.

Would a std::optional object created with no value be as easy to check as
some_opt == std::nullopt?

@Furry Guy, what you say is true (kind of - see end of this post where I check an optional in exactly the same way I check a pointer)

But I don't need to protect against people having trouble understanding how to check an optional object to see if the object is there or not. If they don't know, they can look that up. What I need to protect against is people not even realising that the object might not be there.

I see people dereferencing null pointers because they didn't realise it could be null. Null pointer dereferencing because people didn't realise it could be null happens every day. When they don't have a pointer, but they have a std::optional, it is much more likely that they will realise the object they want might not exist.

That's what I need to protect against, and that's what std::optional gives me that a pointer does not; with std::optional, it's written right there in the code that the object's existence is optional - the word "optional" is on the screen right in front of them where the optional is created or declared, and if they're using a helpful IDE in other places too - and thus massively increases the chances that the person reading the code will realise that.

That said, it's not exactly difficult and it's no more typing:

Check a pointer isn't null:
1
2
3
4
if (the_pointer)
{
  // do something
}


Check std::optional contains an object:
1
2
3
4
if(the_optional)
{
  // do something
}
Last edited on
I guess I must be weird, then, because I would always check to see if a pointer I am about to use is null or not.

Just as I'd check if a std::optional object is empty or not before I try to use it.
I guess I must be weird, then, because I would always check to see if a pointer I am about to use is null or not.


You must be; software crashes every day because people don't check for a pointer being null.
Heh ;)

BTW, I prefer to write out a comparison, if (std::nullopt == the_optional) vs. if (the_optional)

Documents what I am checking and for what.
Last edited on
I even have the people at work who always, always check the pointers switch to std::optional in such cases, unless they can guarantee me that people who sometimes don't check will never, ever work on the same code.

Someone being brilliant and not needing to make the code obvious is nice for that person. Not so nice for the less brilliant who ever have to work on the same code :/
I can't stop laughing, your work experience makes me glad (and sad) I am a solo hobbyist programmer. That way if I screw up something, and I know I will multiple times, I can't blame it on others.
Thanks for pointing out std::optional. I wasn't aware of it.

An interesting thing about it is that it stores the optional item locally. So unless I misunderstand, that means that that sizeof(std::optional<T>) >= sizeof(T). Interesting stuff.
Topic archived. No new replies allowed.