Returning References and Pointers from Functions

Learn about an important consideration when returning pointers and references from functions
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, Unlimited Access
3D art showing a female blacksmith character
Ryan McCombe
Ryan McCombe
Updated

Now that we’re working with pointers and references, we need to talk briefly about memory management.

This is a large topic - we have a full chapter dedicated to it in the intermediate course. For now, we just need to understand an important consideration when we’re using references or pointers to objects created in our function.

Let's demonstrate the problem with a simple program:

#include <iostream>
using namespace std;

int* GetNumber(){
  int Number{1};
  return &Number;
}

int main(){
  int* Result{GetNumber()};
  cout << "Result: " << *Result;
}

We might expect this to log out 1, but that is not the case:

Result: -858993460

Hopefully, the compiler would have given us a warning that we might have been doing something wrong on our return statement:

Address of stack memory associated with
local variable Number returned

Let's delve a little deeper into this warning, as we should understand what’s going on here.

Stack Memory

In the introductory lessons, we covered the idea of the stack, and how our functions each create stack frames.

Stack frames are a form of memory management.

The parameters and local variables that our functions create are stored within these stack frames.

Within our call to GetNumber(), we are allocated memory to store the int we created. We then returned that memory address to our main function:

Diagram showing the return of a pointer from a function

However, stack frames are an automated form of memory management. When the function ends, its stack frame, and all the objects stored within it are destroyed.

We can see this by using a custom type with a destructor:

#include <iostream>
using namespace std;

struct SomeType {
  int Value{1};
  ~SomeType(){ cout << "Destroying\n"; }
};

SomeType* GetObject(){
  SomeType SomeObject;
  return &SomeObject;
}

int main(){
  SomeType* Result{GetObject()};
  cout << "Result: " << Result->Value;
}
Destroying
Result: -858993460

Dangling Pointers and References

When we have a pointer or reference to an object, and that object is then deleted, the pointer is no longer useful. It is sometimes referred to as a dangling pointer or dangling reference.

When an object is destroyed, the memory it was using is freed up for other purposes.

The pointer is still pointing to that memory address - we just don’t know what will be there whenever we dereference it.

Diagram showing a dangling pointer

Why this only applies to references and pointers

In the past, we’ve been creating and returning local variables without issues. When we return something from a stack frame, that value gets moved or copied to the stack frame that called our function.

When the thing we returned was the value, everything worked as expected.

However, when a pointer (or reference) is returned, that pointer can refer to a memory location that is about to be freed. When that pointer is then used, unpredictable things can happen, as the first example demonstrated.

Storage Durations

The objects we create in C++ have several different ways their memory is managed. These are sometimes referred to as storage durations. The scenarios we're covering here - local variables in functions - are examples of what is commonly called automatic storage duration.

In the next course, we cover memory in more detail. This includes techniques for how we can take more control over the lifecycle of our objects.

For now, we just need to be aware of this behavior. When we create variables, we want to create them in a place where they will last as long as they are needed.

If we need a variable to last the full duration of our program, places we can create it include:

  • The global scope
  • Inside the main function
  • Part of an object that was created in one of those locations

Next Chapter: Polymorphism

In the next chapter, we introduce polymorphism - a key technique that lets us ramp up the complexity of our program, without necessarily making our code more complex. We’ll cover:

  • Introduction to Polymorphism: Defining polymorphism in the context of C++ programming and its importance in creating flexible, reusable code.
  • Types of Polymorphism: Discussing the different types, such as compile-time (overloading) and runtime polymorphism (virtual functions).
  • Function Overloading: Covering how functions can be overloaded to implement different behaviors depending on the data type they are called with.
  • Virtual Functions and Inheritance: Exploring how virtual functions enable runtime polymorphism through inheritance hierarchies.
  • Best Practices and Common Pitfalls: Highlighting best practices in implementing polymorphism and common mistakes to avoid.
  • Case Studies and Examples: Including real-world examples and case studies to demonstrate the use of polymorphism in various scenarios.

Was this lesson useful?

Next Lesson

Function Overloading

This lesson provides an in-depth look at function overloading in C++, covering its importance, implementation, and best practices
3D art showing a character using a compass
Ryan McCombe
Ryan McCombe
Updated
Lesson Contents

Returning References and Pointers from Functions

Learn about an important consideration when returning pointers and references from functions

3D art showing a progammer setting up a development environment
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, Unlimited Access
References and Pointers
3D art showing a progammer setting up a development environment
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, unlimited access

This course includes:

  • 56 Lessons
  • Over 200 Quiz Questions
  • 95% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Next Lesson

Function Overloading

This lesson provides an in-depth look at function overloading in C++, covering its importance, implementation, and best practices
3D art showing a character using a compass
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved